| //--------------------------------------------------------------------------- |
| // Greenplum Database |
| // Copyright (C) 2010 Greenplum, Inc. |
| // |
| // @filename: |
| // CTranslatorDXLToPlStmt.cpp |
| // |
| // @doc: |
| // Implementation of the methods for translating from DXL tree to GPDB |
| // PlannedStmt. |
| // |
| // @test: |
| // |
| // |
| //--------------------------------------------------------------------------- |
| |
| extern "C" { |
| #include "postgres.h" |
| |
| #include "access/sysattr.h" |
| #include "catalog/gp_distribution_policy.h" |
| #include "catalog/pg_collation.h" |
| #include "cdb/cdbutil.h" |
| #include "cdb/cdbvars.h" |
| #include "executor/execPartition.h" |
| #include "executor/executor.h" |
| #include "nodes/nodes.h" |
| #include "nodes/plannodes.h" |
| #include "nodes/primnodes.h" |
| #include "partitioning/partdesc.h" |
| #include "storage/lmgr.h" |
| #include "utils/guc.h" |
| #include "optimizer/cost.h" |
| #include "utils/lsyscache.h" |
| #include "utils/partcache.h" |
| #include "utils/rel.h" |
| #include "utils/typcache.h" |
| #include "utils/uri.h" |
| } |
| |
| #include <algorithm> |
| #include <limits> // std::numeric_limits |
| #include <numeric> |
| #include <tuple> |
| |
| #include "gpos/base.h" |
| #include "gpos/common/CBitSet.h" |
| #include "gpos/common/CBitSetIter.h" |
| |
| #include "gpopt/base/CUtils.h" |
| #include "gpopt/gpdbwrappers.h" |
| #include "gpopt/mdcache/CMDAccessor.h" |
| #include "gpopt/translate/CIndexQualInfo.h" |
| #include "gpopt/translate/CPartPruneStepsBuilder.h" |
| #include "gpopt/translate/CTranslatorDXLToPlStmt.h" |
| #include "gpopt/translate/CTranslatorUtils.h" |
| #include "naucrates/dxl/operators/CDXLDatumGeneric.h" |
| #include "naucrates/dxl/operators/CDXLDirectDispatchInfo.h" |
| #include "naucrates/dxl/operators/CDXLNode.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalAgg.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalAppend.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalAssert.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalBitmapTableScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalCTAS.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalCTEConsumer.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalCTEProducer.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalDynamicBitmapTableScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalDynamicForeignScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalDynamicIndexOnlyScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalDynamicIndexScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalDynamicTableScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalGatherMotion.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalHashJoin.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalIndexOnlyScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalLimit.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalMaterialize.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalMergeJoin.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalNLJoin.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalPartitionSelector.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalRedistributeMotion.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalResult.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalRoutedDistributeMotion.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalSort.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalSplit.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalTVF.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalTableScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalParallelTableScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalValuesScan.h" |
| #include "naucrates/dxl/operators/CDXLPhysicalWindow.h" |
| #include "naucrates/dxl/operators/CDXLScalarBitmapBoolOp.h" |
| #include "naucrates/dxl/operators/CDXLScalarBitmapIndexProbe.h" |
| #include "naucrates/dxl/operators/CDXLScalarBoolExpr.h" |
| #include "naucrates/dxl/operators/CDXLScalarFuncExpr.h" |
| #include "naucrates/dxl/operators/CDXLScalarHashExpr.h" |
| #include "naucrates/dxl/operators/CDXLScalarNullTest.h" |
| #include "naucrates/dxl/operators/CDXLScalarOpExpr.h" |
| #include "naucrates/dxl/operators/CDXLScalarProjElem.h" |
| #include "naucrates/dxl/operators/CDXLScalarSortCol.h" |
| #include "naucrates/dxl/operators/CDXLScalarWindowFrameEdge.h" |
| #include "naucrates/exception.h" |
| #include "naucrates/md/IMDAggregate.h" |
| #include "naucrates/md/IMDFunction.h" |
| #include "naucrates/md/IMDIndex.h" |
| #include "naucrates/md/IMDScalarOp.h" |
| #include "naucrates/md/IMDType.h" |
| #include "naucrates/md/IMDTypeBool.h" |
| #include "naucrates/md/IMDTypeInt4.h" |
| #include "naucrates/traceflags/traceflags.h" |
| |
| #include "nodes/nodeFuncs.h" |
| |
| using namespace gpdxl; |
| using namespace gpos; |
| using namespace gpopt; |
| using namespace gpmd; |
| |
| #define GPDXL_ROOT_PLAN_ID -1 |
| #define GPDXL_PLAN_ID_START 1 |
| #define GPDXL_MOTION_ID_START 1 |
| #define GPDXL_PARAM_ID_START 0 |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::CTranslatorDXLToPlStmt |
| // |
| // @doc: |
| // Ctor |
| // |
| //--------------------------------------------------------------------------- |
| CTranslatorDXLToPlStmt::CTranslatorDXLToPlStmt( |
| CMemoryPool *mp, CMDAccessor *md_accessor, |
| CContextDXLToPlStmt *dxl_to_plstmt_context, ULONG num_of_segments) |
| : m_mp(mp), |
| m_md_accessor(md_accessor), |
| m_dxl_to_plstmt_context(dxl_to_plstmt_context), |
| m_cmd_type(CMD_SELECT), |
| m_is_tgt_tbl_distributed(false), |
| m_result_rel_list(nullptr), |
| m_num_of_segments(num_of_segments), |
| m_partition_selector_counter(0) |
| { |
| m_translator_dxl_to_scalar = GPOS_NEW(m_mp) |
| CTranslatorDXLToScalar(m_mp, m_md_accessor, m_num_of_segments); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::~CTranslatorDXLToPlStmt |
| // |
| // @doc: |
| // Dtor |
| // |
| //--------------------------------------------------------------------------- |
| CTranslatorDXLToPlStmt::~CTranslatorDXLToPlStmt() |
| { |
| GPOS_DELETE(m_translator_dxl_to_scalar); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::GetPlannedStmtFromDXL |
| // |
| // @doc: |
| // Translate DXL node into a PlannedStmt |
| // |
| //--------------------------------------------------------------------------- |
| PlannedStmt * |
| CTranslatorDXLToPlStmt::GetPlannedStmtFromDXL(const CDXLNode *dxlnode, |
| const Query *orig_query, |
| bool can_set_tag) |
| { |
| GPOS_ASSERT(nullptr != dxlnode); |
| |
| CDXLTranslateContext dxl_translate_ctxt(m_mp, false, orig_query); |
| |
| PlanSlice *topslice; |
| |
| topslice = (PlanSlice *) gpdb::GPDBAlloc(sizeof(PlanSlice)); |
| memset(topslice, 0, sizeof(PlanSlice)); |
| topslice->sliceIndex = 0; |
| topslice->parentIndex = -1; |
| topslice->gangType = GANGTYPE_UNALLOCATED; |
| topslice->numsegments = 1; |
| topslice->segindex = -1; |
| topslice->directDispatch.isDirectDispatch = false; |
| topslice->directDispatch.contentIds = NIL; |
| topslice->directDispatch.haveProcessedAnyCalculations = false; |
| |
| m_dxl_to_plstmt_context->m_orig_query = (Query *) orig_query; |
| m_dxl_to_plstmt_context->AddSlice(topslice); |
| m_dxl_to_plstmt_context->SetCurrentSlice(topslice); |
| |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| Plan *plan = TranslateDXLOperatorToPlan(dxlnode, &dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| ctxt_translation_prev_siblings->Release(); |
| |
| GPOS_ASSERT(nullptr != plan); |
| |
| // collect oids from rtable |
| List *oids_list = NIL; |
| |
| // collect unique RTE in FROM Clause |
| List *oids_list_unique = NIL; |
| |
| ListCell *lc_rte = nullptr; |
| |
| RangeTblEntry *pRTEHashFuncCal = nullptr; |
| |
| ForEach(lc_rte, m_dxl_to_plstmt_context->GetRTableEntriesList()) |
| { |
| RangeTblEntry *pRTE = (RangeTblEntry *) lfirst(lc_rte); |
| |
| if (pRTE->rtekind == RTE_RELATION) |
| { |
| oids_list = gpdb::LAppendOid(oids_list, pRTE->relid); |
| if (pRTE->inFromCl || (CMD_INSERT == m_cmd_type)) |
| { |
| // If we have only one RTE in the FROM clause, |
| // then we use it to extract information |
| // about the distribution policy, which gives info about the |
| // typeOid used for direct dispatch. This helps to perform |
| // direct dispatch based on the distribution column type |
| // inplace of the constant in the filter. |
| pRTEHashFuncCal = (RangeTblEntry *) lfirst(lc_rte); |
| |
| // collecting only unique RTE in FROM clause |
| oids_list_unique = |
| list_append_unique_oid(oids_list_unique, pRTE->relid); |
| } |
| } |
| } |
| |
| if (gpdb::ListLength(oids_list_unique) > 1) |
| { |
| // If we have a scenario with multiple unique RTE |
| // in "from" clause, then the hash function selection |
| // based on distribution policy of relation will not work |
| // and we switch back to selection based on constant type |
| pRTEHashFuncCal = nullptr; |
| } |
| |
| // assemble planned stmt |
| PlannedStmt *planned_stmt = MakeNode(PlannedStmt); |
| planned_stmt->planGen = PLANGEN_OPTIMIZER; |
| |
| planned_stmt->rtable = m_dxl_to_plstmt_context->GetRTableEntriesList(); |
| planned_stmt->subplans = m_dxl_to_plstmt_context->GetSubplanEntriesList(); |
| planned_stmt->planTree = plan; |
| |
| planned_stmt->canSetTag = can_set_tag; |
| planned_stmt->relationOids = oids_list; |
| |
| planned_stmt->commandType = m_cmd_type; |
| |
| planned_stmt->resultRelations = m_result_rel_list; |
| planned_stmt->intoPolicy = m_dxl_to_plstmt_context->GetDistributionPolicy(); |
| |
| planned_stmt->paramExecTypes = m_dxl_to_plstmt_context->GetParamTypes(); |
| planned_stmt->slices = |
| m_dxl_to_plstmt_context->GetSlices(&planned_stmt->numSlices); |
| planned_stmt->subplan_sliceIds = |
| m_dxl_to_plstmt_context->GetSubplanSliceIdArray(); |
| |
| topslice = &planned_stmt->slices[0]; |
| |
| // Can we do direct dispatch? |
| if (CMD_SELECT == m_cmd_type && |
| nullptr != dxlnode->GetDXLDirectDispatchInfo()) |
| { |
| List *direct_dispatch_segids = TranslateDXLDirectDispatchInfo( |
| dxlnode->GetDXLDirectDispatchInfo(), pRTEHashFuncCal); |
| |
| if (direct_dispatch_segids != NIL) |
| { |
| for (int i = 0; i < planned_stmt->numSlices; i++) |
| { |
| PlanSlice *slice = &planned_stmt->slices[i]; |
| |
| slice->directDispatch.isDirectDispatch = true; |
| slice->directDispatch.contentIds = direct_dispatch_segids; |
| } |
| } |
| } |
| |
| if ((CMD_INSERT == m_cmd_type || CMD_DELETE == m_cmd_type) && |
| planned_stmt->numSlices == 1 && |
| dxlnode->GetOperator()->GetDXLOperator() == EdxlopPhysicalDML) |
| { |
| CDXLPhysicalDML *phy_dml_dxlop = |
| CDXLPhysicalDML::Cast(dxlnode->GetOperator()); |
| |
| List *direct_dispatch_segids = TranslateDXLDirectDispatchInfo( |
| phy_dml_dxlop->GetDXLDirectDispatchInfo(), pRTEHashFuncCal); |
| if (direct_dispatch_segids != NIL) |
| { |
| topslice->directDispatch.isDirectDispatch = true; |
| topslice->directDispatch.contentIds = direct_dispatch_segids; |
| } |
| } |
| |
| /* |
| * If it's a CREATE TABLE AS, we have to dispatch the top slice to |
| * all segments, because the catalog changes need to be made |
| * everywhere even if the data originates from only some segments. |
| */ |
| if (orig_query->commandType == CMD_SELECT && |
| orig_query->parentStmtType == PARENTSTMTTYPE_CTAS) |
| { |
| topslice->numsegments = m_num_of_segments; |
| topslice->gangType = GANGTYPE_PRIMARY_WRITER; |
| } |
| |
| return planned_stmt; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLOperatorToPlan |
| // |
| // @doc: |
| // Translates a DXL tree into a Plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLOperatorToPlan( |
| const CDXLNode *dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| GPOS_ASSERT(nullptr != dxlnode); |
| GPOS_ASSERT(nullptr != ctxt_translation_prev_siblings); |
| |
| Plan *plan; |
| |
| const CDXLOperator *dxlop = dxlnode->GetOperator(); |
| gpdxl::Edxlopid ulOpId = dxlop->GetDXLOperator(); |
| |
| switch (ulOpId) |
| { |
| default: |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtConversion, |
| dxlnode->GetOperator()->GetOpNameStr()->GetBuffer()); |
| } |
| case EdxlopPhysicalTableScan: |
| case EdxlopPhysicalForeignScan: |
| { |
| plan = TranslateDXLTblScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalParallelTableScan: |
| { |
| plan = TranslateDXLParallelTblScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalIndexScan: |
| { |
| plan = TranslateDXLIndexScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalIndexOnlyScan: |
| { |
| plan = TranslateDXLIndexOnlyScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalHashJoin: |
| { |
| plan = TranslateDXLHashJoin(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalNLJoin: |
| { |
| plan = TranslateDXLNLJoin(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalMergeJoin: |
| { |
| plan = TranslateDXLMergeJoin(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalMotionGather: |
| case EdxlopPhysicalMotionBroadcast: |
| case EdxlopPhysicalMotionRoutedDistribute: |
| { |
| plan = TranslateDXLMotion(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalMotionRedistribute: |
| case EdxlopPhysicalMotionRandom: |
| { |
| plan = TranslateDXLDuplicateSensitiveMotion( |
| dxlnode, output_context, ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalLimit: |
| { |
| plan = TranslateDXLLimit(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalAgg: |
| { |
| plan = TranslateDXLAgg(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalWindow: |
| { |
| if (CDXLPhysicalWindow::Cast(dxlnode->GetOperator())->IsWindowHashAgg()) { |
| plan = TranslateDXLWindowHashAgg(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| } else { |
| plan = TranslateDXLWindowAgg(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| } |
| break; |
| } |
| case EdxlopPhysicalSort: |
| { |
| plan = TranslateDXLSort(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalResult: |
| { |
| plan = TranslateDXLResult(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalAppend: |
| { |
| plan = TranslateDXLAppend(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalMaterialize: |
| { |
| plan = TranslateDXLMaterialize(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalSequence: |
| { |
| plan = TranslateDXLSequence(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalDynamicTableScan: |
| { |
| plan = TranslateDXLDynTblScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalDynamicIndexScan: |
| { |
| plan = TranslateDXLDynIdxScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalDynamicIndexOnlyScan: |
| { |
| plan = TranslateDXLDynIdxOnlyScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalDynamicForeignScan: |
| { |
| plan = TranslateDXLDynForeignScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalTVF: |
| { |
| plan = TranslateDXLTvf(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalDML: |
| { |
| plan = TranslateDXLDml(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalSplit: |
| { |
| plan = TranslateDXLSplit(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalAssert: |
| { |
| plan = TranslateDXLAssert(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalCTEProducer: |
| { |
| plan = TranslateDXLCTEProducerToSharedScan( |
| dxlnode, output_context, ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalCTEConsumer: |
| { |
| plan = TranslateDXLCTEConsumerToSharedScan( |
| dxlnode, output_context, ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalBitmapTableScan: |
| case EdxlopPhysicalDynamicBitmapTableScan: |
| { |
| plan = TranslateDXLBitmapTblScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalCTAS: |
| { |
| plan = TranslateDXLCtas(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalPartitionSelector: |
| { |
| plan = TranslateDXLPartSelector(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| case EdxlopPhysicalValuesScan: |
| { |
| plan = TranslateDXLValueScan(dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| break; |
| } |
| } |
| |
| if (nullptr == plan) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtConversion, |
| dxlnode->GetOperator()->GetOpNameStr()->GetBuffer()); |
| } |
| return plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::SetParamIds |
| // |
| // @doc: |
| // Set the bitmapset with the param_ids defined in the plan |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::SetParamIds(Plan *plan) |
| { |
| List *params_node_list = gpdb::ExtractNodesPlan( |
| plan, T_Param, true /* descend_into_subqueries */); |
| |
| ListCell *lc = nullptr; |
| |
| Bitmapset *bitmapset = nullptr; |
| |
| ForEach(lc, params_node_list) |
| { |
| Param *param = (Param *) lfirst(lc); |
| bitmapset = gpdb::BmsAddMember(bitmapset, param->paramid); |
| } |
| |
| plan->extParam = bitmapset; |
| plan->allParam = bitmapset; |
| } |
| |
| List * |
| CTranslatorDXLToPlStmt::TranslatePartOids(IMdIdArray *parts, INT lockmode) |
| { |
| 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); |
| // Since parser locks only root partition, locking the leaf |
| // partitions which we have to scan. |
| gpdb::GPDBLockRelationOid(part, lockmode); |
| } |
| return oids_list; |
| } |
| |
| List * |
| CTranslatorDXLToPlStmt::TranslateJoinPruneParamids( |
| const ULongPtrArray *selector_ids, OID oid_type, |
| CContextDXLToPlStmt *dxl_to_plstmt_context) |
| { |
| List *join_prune_paramids = NIL; |
| |
| for (ULONG ul = 0; ul < selector_ids->Size(); ++ul) |
| { |
| ULONG selector_id = *(*selector_ids)[ul]; |
| ULONG param_id = |
| dxl_to_plstmt_context->GetParamIdForSelector(oid_type, selector_id); |
| join_prune_paramids = gpdb::LAppendInt(join_prune_paramids, param_id); |
| } |
| return join_prune_paramids; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @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()); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| 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) |
| OID oidRel = CMDIdGPDB::CastMdid(md_rel->MDId())->Oid(); |
| GPOS_ASSERT(dxl_table_descr->LockMode() != -1); |
| gpdb::GPDBLockRelationOid(oidRel, dxl_table_descr->LockMode()); |
| |
| Index index = ProcessDXLTblDescr(dxl_table_descr, &base_table_context); |
| |
| // a table scan node must have 2 children: projection list and filter |
| GPOS_ASSERT(2 == tbl_scan_dxlnode->Arity()); |
| |
| // translate proj list and filter |
| CDXLNode *project_list_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexProjList]; |
| CDXLNode *filter_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexFilter]; |
| |
| List *targetlist = NIL; |
| |
| // List to hold the quals after translating filter_dxlnode node. |
| List *query_quals = NIL; |
| |
| TranslateProjListAndFilter( |
| project_list_dxlnode, filter_dxlnode, |
| &base_table_context, // translate context for the base table |
| nullptr, // translate_ctxt_left and pdxltrctxRight, |
| &targetlist, &query_quals, output_context); |
| |
| Plan *plan = nullptr; |
| Plan *plan_return = nullptr; |
| |
| if (IMDRelation::ErelstorageForeign == md_rel->RetrieveRelStorageType()) |
| { |
| RangeTblEntry *rte = m_dxl_to_plstmt_context->GetRTEByIndex(index); |
| |
| // The postgres_fdw wrapper does not support row level security. So |
| // passing only the query_quals while creating the foreign scan node. |
| ForeignScan *foreign_scan = |
| gpdb::CreateForeignScan(oidRel, index, query_quals, 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; |
| |
| // List to hold the quals which contain both security quals and query |
| // quals. |
| List *security_query_quals = NIL; |
| |
| // Fetching the RTE of the relation from the rewritten parse tree |
| // based on the oidRel and adding the security quals of the RTE in |
| // the security_query_quals list. |
| AddSecurityQuals(oidRel, &security_query_quals, &index); |
| |
| // The security quals should always be executed first when |
| // compared to other quals. So appending query quals to the |
| // security_query_quals list after the security quals. |
| security_query_quals = |
| gpdb::ListConcat(security_query_quals, query_quals); |
| plan->qual = security_query_quals; |
| } |
| |
| if (md_rel->IsNonBlockTable()) |
| { |
| CheckSafeTargetListForAOTables(plan->targetlist); |
| } |
| |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(tbl_scan_dxlnode, plan); |
| |
| SetParamIds(plan); |
| |
| return plan_return; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLParallelTblScan |
| // |
| // @doc: |
| // Translates a DXL parallel table scan node into a parallel SeqScan node |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLParallelTblScan( |
| const CDXLNode *tbl_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray * /*ctxt_translation_prev_siblings*/) |
| { |
| // translate table descriptor into a range table entry |
| CDXLPhysicalParallelTableScan *phy_parallel_tbl_scan_dxlop = |
| CDXLPhysicalParallelTableScan::Cast(tbl_scan_dxlnode->GetOperator()); |
| |
| ULONG parallel_workers = phy_parallel_tbl_scan_dxlop->UlParallelWorkers(); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const CDXLTableDescr *dxl_table_descr = |
| phy_parallel_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) |
| OID oidRel = CMDIdGPDB::CastMdid(md_rel->MDId())->Oid(); |
| GPOS_ASSERT(dxl_table_descr->LockMode() != -1); |
| gpdb::GPDBLockRelationOid(oidRel, dxl_table_descr->LockMode()); |
| |
| Index index = ProcessDXLTblDescr(dxl_table_descr, &base_table_context); |
| |
| // a table scan node must have 2 children: projection list and filter |
| GPOS_ASSERT(2 == tbl_scan_dxlnode->Arity()); |
| |
| // translate proj list and filter |
| CDXLNode *project_list_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexProjList]; |
| CDXLNode *filter_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexFilter]; |
| |
| List *targetlist = NIL; |
| |
| // List to hold the quals after translating filter_dxlnode node. |
| List *query_quals = NIL; |
| |
| TranslateProjListAndFilter( |
| project_list_dxlnode, filter_dxlnode, |
| &base_table_context, // translate context for the base table |
| nullptr, // translate_ctxt_left and pdxltrctxRight, |
| &targetlist, &query_quals, output_context); |
| |
| Plan *plan = nullptr; |
| Plan *plan_return = nullptr; |
| |
| // Parallel table scans are always sequential scans (not foreign scans) |
| SeqScan *seq_scan = MakeNode(SeqScan); |
| seq_scan->scanrelid = index; |
| plan = &(seq_scan->plan); |
| plan_return = (Plan *) seq_scan; |
| |
| // Set parallel execution flags |
| plan->parallel_aware = true; |
| plan->parallel_safe = true; |
| plan->parallel = (int) parallel_workers; |
| |
| plan->targetlist = targetlist; |
| |
| // List to hold the quals which contain both security quals and query |
| // quals. |
| List *security_query_quals = NIL; |
| |
| // Fetching the RTE of the relation from the rewritten parse tree |
| // based on the oidRel and adding the security quals of the RTE in |
| // the security_query_quals list. |
| AddSecurityQuals(oidRel, &security_query_quals, &index); |
| |
| // The security quals should always be executed first when |
| // compared to other quals. So appending query quals to the |
| // security_query_quals list after the security quals. |
| security_query_quals = |
| gpdb::ListConcat(security_query_quals, query_quals); |
| plan->qual = security_query_quals; |
| |
| if (md_rel->IsNonBlockTable()) |
| { |
| CheckSafeTargetListForAOTables(plan->targetlist); |
| } |
| |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(tbl_scan_dxlnode, plan); |
| |
| // Adjust row count to per-worker statistics |
| if (parallel_workers > 1) |
| { |
| plan->plan_rows = ceil(plan->plan_rows / parallel_workers); |
| } |
| |
| SetParamIds(plan); |
| |
| return plan_return; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::SetIndexVarAttnoWalker |
| // |
| // @doc: |
| // Walker to set index var attno's, |
| // attnos of index vars are set to their relative positions in index keys, |
| // |
| //--------------------------------------------------------------------------- |
| BOOL |
| CTranslatorDXLToPlStmt::SetIndexVarAttnoWalker( |
| Node *node, SContextIndexVarAttno *ctxt_index_var_attno_walker) |
| { |
| if (nullptr == node) |
| { |
| return false; |
| } |
| |
| if (IsA(node, Var) && ((Var *) node)->varno != OUTER_VAR) |
| { |
| INT attno = ((Var *) node)->varattno; |
| const IMDRelation *md_rel = ctxt_index_var_attno_walker->m_md_rel; |
| const IMDIndex *index = ctxt_index_var_attno_walker->m_md_index; |
| |
| ULONG index_col_pos_idx_max = gpos::ulong_max; |
| const ULONG arity = md_rel->ColumnCount(); |
| for (ULONG col_pos_idx = 0; col_pos_idx < arity; col_pos_idx++) |
| { |
| const IMDColumn *md_col = md_rel->GetMdCol(col_pos_idx); |
| if (attno == md_col->AttrNum()) |
| { |
| index_col_pos_idx_max = col_pos_idx; |
| break; |
| } |
| } |
| |
| if (gpos::ulong_max > index_col_pos_idx_max) |
| { |
| ((Var *) node)->varattno = |
| 1 + index->GetKeyPos(index_col_pos_idx_max); |
| } |
| |
| return false; |
| } |
| |
| return gpdb::WalkExpressionTree( |
| node, (BOOL(*)(Node *, void *)) CTranslatorDXLToPlStmt::SetIndexVarAttnoWalker, |
| ctxt_index_var_attno_walker); |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLIndexScan |
| // |
| // @doc: |
| // Translates a DXL index scan node into a IndexScan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLIndexScan( |
| const CDXLNode *index_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // translate table descriptor into a range table entry |
| CDXLPhysicalIndexScan *physical_idx_scan_dxlop = |
| CDXLPhysicalIndexScan::Cast(index_scan_dxlnode->GetOperator()); |
| |
| return TranslateDXLIndexScan(index_scan_dxlnode, physical_idx_scan_dxlop, |
| output_context, |
| ctxt_translation_prev_siblings); |
| } |
| |
| void |
| CTranslatorDXLToPlStmt::TranslatePlan( |
| Plan *plan, const CDXLNode *dxlnode, CDXLTranslateContext *output_context, |
| CContextDXLToPlStmt *dxl_to_plstmt_context, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| plan->plan_node_id = dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(dxlnode, plan); |
| |
| // an index scan node must have 3 children: projection list, filter and index condition list |
| GPOS_ASSERT(3 == dxlnode->Arity()); |
| |
| // translate proj list and filter |
| CDXLNode *project_list_dxlnode = (*dxlnode)[EdxlisIndexProjList]; |
| CDXLNode *filter_dxlnode = (*dxlnode)[EdxlisIndexFilter]; |
| |
| // translate proj list |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, base_table_context, |
| nullptr /*child_contexts*/, output_context); |
| |
| // translate index filter |
| plan->qual = TranslateDXLIndexFilter(filter_dxlnode, output_context, |
| base_table_context, |
| ctxt_translation_prev_siblings); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLIndexScan |
| // |
| // @doc: |
| // Translates a DXL index scan node into a IndexScan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLIndexScan( |
| const CDXLNode *index_scan_dxlnode, |
| CDXLPhysicalIndexScan *physical_idx_scan_dxlop, |
| CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const CDXLTableDescr *dxl_table_descr = |
| physical_idx_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()); |
| GPOS_ASSERT(dxl_table_descr->LockMode() != -1); |
| gpdb::GPDBLockRelationOid(mdid->Oid(), dxl_table_descr->LockMode()); |
| |
| Index index = ProcessDXLTblDescr(dxl_table_descr, &base_table_context); |
| |
| IndexScan *index_scan = nullptr; |
| index_scan = MakeNode(IndexScan); |
| index_scan->scan.scanrelid = index; |
| |
| CMDIdGPDB *mdid_index = CMDIdGPDB::CastMdid( |
| physical_idx_scan_dxlop->GetDXLIndexDescr()->MDId()); |
| const IMDIndex *md_index = m_md_accessor->RetrieveIndex(mdid_index); |
| Oid index_oid = mdid_index->Oid(); |
| |
| GPOS_ASSERT(InvalidOid != index_oid); |
| // Lock any index we are to scan, since it may not have been properly locked |
| // by the parser (e.g in case of generated scans for partitioned indexes) |
| gpdb::GPDBLockRelationOid(index_oid, dxl_table_descr->LockMode()); |
| index_scan->indexid = index_oid; |
| |
| Plan *plan = &(index_scan->scan.plan); |
| |
| TranslatePlan(plan, index_scan_dxlnode, output_context, |
| m_dxl_to_plstmt_context, &base_table_context, |
| ctxt_translation_prev_siblings); |
| |
| index_scan->indexorderdir = CTranslatorUtils::GetScanDirection( |
| physical_idx_scan_dxlop->GetIndexScanDir()); |
| |
| if (md_rel->IsNonBlockTable()) |
| { |
| CheckSafeTargetListForAOTables(plan->targetlist); |
| } |
| |
| // translate index condition list |
| List *index_cond = NIL; |
| List *index_orig_cond = NIL; |
| |
| // Translate Index Conditions if Index isn't used for order by. |
| if (!IsIndexForOrderBy(&base_table_context, ctxt_translation_prev_siblings, |
| output_context, |
| (*index_scan_dxlnode)[EdxlisIndexCondition])) |
| { |
| TranslateIndexConditions( |
| (*index_scan_dxlnode)[EdxlisIndexCondition], |
| physical_idx_scan_dxlop->GetDXLTableDescr(), |
| false, // is_bitmap_index_probe |
| md_index, md_rel, output_context, &base_table_context, |
| ctxt_translation_prev_siblings, &index_cond, &index_orig_cond); |
| } |
| |
| index_scan->indexqual = index_cond; |
| index_scan->indexqualorig = index_orig_cond; |
| /* |
| * As of 8.4, the indexstrategy and indexsubtype fields are no longer |
| * available or needed in IndexScan. Ignore them. |
| */ |
| SetParamIds(plan); |
| |
| return (Plan *) index_scan; |
| } |
| |
| static List * |
| TranslateDXLIndexTList(const IMDRelation *md_rel, const IMDIndex *md_index, |
| Index new_varno, const CDXLTableDescr *table_descr, |
| CDXLTranslateContextBaseTable *index_context) |
| { |
| List *target_list = NIL; |
| |
| index_context->SetRelIndex(INDEX_VAR); |
| |
| // Translate KEY columns |
| for (ULONG ul = 0; ul < md_index->Keys(); ul++) |
| { |
| ULONG key = md_index->KeyAt(ul); |
| |
| const IMDColumn *col = md_rel->GetMdCol(key); |
| |
| TargetEntry *target_entry = MakeNode(TargetEntry); |
| target_entry->resno = (AttrNumber) ul + 1; |
| |
| Expr *indexvar = (Expr *) gpdb::MakeVar( |
| new_varno, col->AttrNum(), |
| CMDIdGPDB::CastMdid(col->MdidType())->Oid(), |
| col->TypeModifier() /*vartypmod*/, 0 /*varlevelsup*/); |
| target_entry->expr = indexvar; |
| |
| // Fix up proj list. Since index only scan does not read full tuples, |
| // the var->varattno must be updated as it should no longer point to |
| // column in the table, but rather a column in the index. We achieve |
| // this by mapping col id to a new varattno based on index columns. |
| for (ULONG j = 0; j < table_descr->Arity(); j++) |
| { |
| const CDXLColDescr *dxl_col_descr = |
| table_descr->GetColumnDescrAt(j); |
| if (dxl_col_descr->AttrNum() == ((Var *) indexvar)->varattno) |
| { |
| (void) index_context->InsertMapping(dxl_col_descr->Id(), |
| ul + 1); |
| break; |
| } |
| } |
| |
| target_list = gpdb::LAppend(target_list, target_entry); |
| } |
| |
| // Translate INCLUDED columns |
| for (ULONG ul = 0; ul < md_index->IncludedCols(); ul++) |
| { |
| ULONG includecol = md_index->IncludedColAt(ul); |
| |
| const IMDColumn *col = md_rel->GetMdCol(includecol); |
| |
| TargetEntry *target_entry = MakeNode(TargetEntry); |
| // KEY columns preceed INCLUDE columns |
| target_entry->resno = (AttrNumber) ul + 1 + md_index->Keys(); |
| |
| Expr *indexvar = (Expr *) gpdb::MakeVar( |
| new_varno, col->AttrNum(), |
| CMDIdGPDB::CastMdid(col->MdidType())->Oid(), |
| col->TypeModifier() /*vartypmod*/, 0 /*varlevelsup*/); |
| target_entry->expr = indexvar; |
| |
| for (ULONG j = 0; j < table_descr->Arity(); j++) |
| { |
| const CDXLColDescr *dxl_col_descr = |
| table_descr->GetColumnDescrAt(j); |
| if (dxl_col_descr->AttrNum() == ((Var *) indexvar)->varattno) |
| { |
| (void) index_context->InsertMapping(dxl_col_descr->Id(), |
| target_entry->resno); |
| break; |
| } |
| } |
| |
| target_list = gpdb::LAppend(target_list, target_entry); |
| } |
| |
| return target_list; |
| } |
| |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLIndexOnlyScan( |
| const CDXLNode *index_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // translate table descriptor into a range table entry |
| CDXLPhysicalIndexOnlyScan *physical_idx_scan_dxlop = |
| CDXLPhysicalIndexOnlyScan::Cast(index_scan_dxlnode->GetOperator()); |
| const CDXLTableDescr *table_desc = |
| physical_idx_scan_dxlop->GetDXLTableDescr(); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel( |
| physical_idx_scan_dxlop->GetDXLTableDescr()->MDId()); |
| |
| Index index = ProcessDXLTblDescr(table_desc, &base_table_context); |
| |
| IndexOnlyScan *index_scan = MakeNode(IndexOnlyScan); |
| index_scan->scan.scanrelid = index; |
| |
| CMDIdGPDB *mdid_index = CMDIdGPDB::CastMdid( |
| physical_idx_scan_dxlop->GetDXLIndexDescr()->MDId()); |
| const IMDIndex *md_index = m_md_accessor->RetrieveIndex(mdid_index); |
| Oid index_oid = mdid_index->Oid(); |
| |
| GPOS_ASSERT(InvalidOid != index_oid); |
| index_scan->indexid = index_oid; |
| |
| CDXLTranslateContextBaseTable index_context(m_mp); |
| |
| // translate index targetlist |
| index_scan->indextlist = TranslateDXLIndexTList(md_rel, md_index, index, |
| table_desc, &index_context); |
| |
| Plan *plan = &(index_scan->scan.plan); |
| TranslatePlan(plan, index_scan_dxlnode, output_context, |
| m_dxl_to_plstmt_context, &index_context, |
| ctxt_translation_prev_siblings); |
| |
| index_scan->indexorderdir = CTranslatorUtils::GetScanDirection( |
| physical_idx_scan_dxlop->GetIndexScanDir()); |
| |
| // translate index condition list |
| List *index_cond = NIL; |
| List *index_orig_cond = NIL; |
| |
| // Translate Index Conditions if Index isn't used for order by. |
| if (!IsIndexForOrderBy(&base_table_context, ctxt_translation_prev_siblings, |
| output_context, |
| (*index_scan_dxlnode)[EdxlisIndexCondition])) |
| { |
| TranslateIndexConditions( |
| (*index_scan_dxlnode)[EdxlisIndexCondition], |
| physical_idx_scan_dxlop->GetDXLTableDescr(), |
| false, // is_bitmap_index_probe |
| md_index, md_rel, output_context, &base_table_context, |
| ctxt_translation_prev_siblings, &index_cond, &index_orig_cond); |
| } |
| |
| index_scan->indexqual = index_cond; |
| SetParamIds(plan); |
| |
| return (Plan *) index_scan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateIndexFilter |
| // |
| // @doc: |
| // Translate the index filter list in an Index scan |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLIndexFilter( |
| CDXLNode *filter_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| List *quals_list = NIL; |
| |
| // build colid->var mapping |
| CMappingColIdVarPlStmt colid_var_mapping( |
| m_mp, base_table_context, ctxt_translation_prev_siblings, |
| output_context, m_dxl_to_plstmt_context); |
| |
| const ULONG arity = filter_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *index_filter_dxlnode = (*filter_dxlnode)[ul]; |
| Expr *index_filter_expr = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| index_filter_dxlnode, &colid_var_mapping); |
| quals_list = gpdb::LAppend(quals_list, index_filter_expr); |
| } |
| |
| return quals_list; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateIndexConditions |
| // |
| // @doc: |
| // Translate the index condition list in an Index scan |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslateIndexConditions( |
| CDXLNode *index_cond_list_dxlnode, const CDXLTableDescr * /*dxl_tbl_descr*/, |
| BOOL is_bitmap_index_probe, const IMDIndex *index, |
| const IMDRelation *md_rel, CDXLTranslateContext *output_context, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings, |
| List **index_cond, List **index_orig_cond) |
| { |
| // array of index qual info |
| CIndexQualInfoArray *index_qual_info_array = |
| GPOS_NEW(m_mp) CIndexQualInfoArray(m_mp); |
| |
| // build colid->var mapping |
| CMappingColIdVarPlStmt colid_var_mapping( |
| m_mp, base_table_context, ctxt_translation_prev_siblings, |
| output_context, m_dxl_to_plstmt_context); |
| |
| const ULONG arity = index_cond_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *index_cond_dxlnode = (*index_cond_list_dxlnode)[ul]; |
| CDXLNode *modified_null_test_cond_dxlnode = nullptr; |
| |
| // FIXME: Remove this translation from BoolExpr to NullTest when ORCA gets rid of |
| // translation of 'x IS NOT NULL' to 'NOT (x IS NULL)'. Here's the ticket that tracks |
| // the issue: https://github.com/greenplum-db/gpdb/issues/16294 |
| |
| // Translate index condition CDXLScalarBoolExpr of format 'NOT (col IS NULL)' |
| // to CDXLScalarNullTest 'col IS NOT NULL', because IndexScan only |
| // supports indexquals of types: OpExpr, RowCompareExpr, |
| // ScalarArrayOpExpr and NullTest |
| if (index_cond_dxlnode->GetOperator()->GetDXLOperator() == |
| EdxlopScalarBoolExpr) |
| { |
| CDXLScalarBoolExpr *boolexpr_dxlop = |
| CDXLScalarBoolExpr::Cast(index_cond_dxlnode->GetOperator()); |
| if (boolexpr_dxlop->GetDxlBoolTypeStr() == Edxlnot && |
| (*index_cond_dxlnode)[0]->GetOperator()->GetDXLOperator() == |
| EdxlopScalarNullTest) |
| { |
| CDXLNode *null_test_cond_dxlnode = (*index_cond_dxlnode)[0]; |
| CDXLNode *scalar_ident_dxlnode = (*null_test_cond_dxlnode)[0]; |
| scalar_ident_dxlnode->AddRef(); |
| modified_null_test_cond_dxlnode = GPOS_NEW(m_mp) CDXLNode( |
| m_mp, GPOS_NEW(m_mp) CDXLScalarNullTest(m_mp, false), |
| scalar_ident_dxlnode); |
| index_cond_dxlnode = modified_null_test_cond_dxlnode; |
| } |
| } |
| Expr *original_index_cond_expr = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| index_cond_dxlnode, &colid_var_mapping); |
| Expr *index_cond_expr = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| index_cond_dxlnode, &colid_var_mapping); |
| GPOS_ASSERT( |
| (IsA(index_cond_expr, OpExpr) || |
| IsA(index_cond_expr, ScalarArrayOpExpr) || |
| IsA(index_cond_expr, NullTest)) && |
| "expected OpExpr or ScalarArrayOpExpr or NullTest in index qual"); |
| |
| // allow Index quals with scalar array only for bitmap and btree indexes |
| if (!is_bitmap_index_probe && IsA(index_cond_expr, ScalarArrayOpExpr) && |
| !(IMDIndex::EmdindBitmap == index->IndexType() || |
| IMDIndex::EmdindBtree == index->IndexType())) |
| { |
| GPOS_RAISE( |
| gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtConversion, |
| GPOS_WSZ_LIT("ScalarArrayOpExpr condition on index scan")); |
| } |
| |
| // We need to perform mapping of Varattnos relative to column positions in index keys |
| SContextIndexVarAttno index_varattno_ctxt(md_rel, index); |
| SetIndexVarAttnoWalker((Node *) index_cond_expr, &index_varattno_ctxt); |
| |
| // find index key's attno |
| List *args_list = nullptr; |
| if (IsA(index_cond_expr, OpExpr)) |
| { |
| args_list = ((OpExpr *) index_cond_expr)->args; |
| } |
| else if (IsA(index_cond_expr, ScalarArrayOpExpr)) |
| { |
| args_list = ((ScalarArrayOpExpr *) index_cond_expr)->args; |
| } |
| else |
| { |
| // NullTest struct doesn't have List argument, hence ignoring |
| // assignment for that type |
| } |
| |
| Node *left_arg; |
| Node *right_arg; |
| if (IsA(index_cond_expr, NullTest)) |
| { |
| // NullTest only has one arg |
| left_arg = (Node *) (((NullTest *) index_cond_expr)->arg); |
| right_arg = nullptr; |
| } |
| else |
| { |
| left_arg = (Node *) lfirst(gpdb::ListHead(args_list)); |
| right_arg = (Node *) lfirst(gpdb::ListTail(args_list)); |
| // Type Coercion doesn't add much value for IS NULL and IS NOT NULL |
| // conditions, and is not supported by ORCA currently |
| BOOL is_relabel_type = false; |
| if (IsA(left_arg, RelabelType) && |
| IsA(((RelabelType *) left_arg)->arg, Var)) |
| { |
| left_arg = (Node *) ((RelabelType *) left_arg)->arg; |
| is_relabel_type = true; |
| } |
| else if (IsA(right_arg, RelabelType) && |
| IsA(((RelabelType *) right_arg)->arg, Var)) |
| { |
| right_arg = (Node *) ((RelabelType *) right_arg)->arg; |
| is_relabel_type = true; |
| } |
| |
| if (is_relabel_type) |
| { |
| List *new_args_list = ListMake2(left_arg, right_arg); |
| gpdb::GPDBFree(args_list); |
| if (IsA(index_cond_expr, OpExpr)) |
| { |
| ((OpExpr *) index_cond_expr)->args = new_args_list; |
| } |
| else |
| { |
| ((ScalarArrayOpExpr *) index_cond_expr)->args = |
| new_args_list; |
| } |
| } |
| } |
| |
| GPOS_ASSERT((IsA(left_arg, Var) || IsA(right_arg, Var)) && |
| "expected index key in index qual"); |
| |
| INT attno = 0; |
| if (IsA(left_arg, Var) && ((Var *) left_arg)->varno != OUTER_VAR) |
| { |
| // index key is on the left side |
| attno = ((Var *) left_arg)->varattno; |
| // GPDB_92_MERGE_FIXME: helluva hack |
| // Upstream commit a0185461 cleaned up how the varno of indices |
| // We are patching up varno here, but it seems this really should |
| // happen in CTranslatorDXLToScalar::PexprFromDXLNodeScalar . |
| // Furthermore, should we guard against nonsensical varno? |
| ((Var *) left_arg)->varno = INDEX_VAR; |
| } |
| else |
| { |
| // index key is on the right side |
| GPOS_ASSERT(((Var *) right_arg)->varno != OUTER_VAR && |
| "unexpected outer reference in index qual"); |
| attno = ((Var *) right_arg)->varattno; |
| } |
| |
| // create index qual |
| index_qual_info_array->Append(GPOS_NEW(m_mp) CIndexQualInfo( |
| attno, index_cond_expr, original_index_cond_expr)); |
| |
| if (modified_null_test_cond_dxlnode != nullptr) |
| { |
| modified_null_test_cond_dxlnode->Release(); |
| } |
| } |
| |
| // the index quals much be ordered by attribute number |
| index_qual_info_array->Sort(CIndexQualInfo::IndexQualInfoCmp); |
| |
| ULONG length = index_qual_info_array->Size(); |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CIndexQualInfo *index_qual_info = (*index_qual_info_array)[ul]; |
| *index_cond = gpdb::LAppend(*index_cond, index_qual_info->m_expr); |
| *index_orig_cond = |
| gpdb::LAppend(*index_orig_cond, index_qual_info->m_original_expr); |
| } |
| |
| // clean up |
| index_qual_info_array->Release(); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLAssertConstraints |
| // |
| // @doc: |
| // Translate the constraints from an Assert node into a list of quals |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLAssertConstraints( |
| CDXLNode *assert_contraint_list_dxlnode, |
| CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *child_contexts) |
| { |
| List *quals_list = NIL; |
| |
| // build colid->var mapping |
| CMappingColIdVarPlStmt colid_var_mapping( |
| m_mp, nullptr /*base_table_context*/, child_contexts, output_context, |
| m_dxl_to_plstmt_context); |
| |
| const ULONG arity = assert_contraint_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *assert_contraint_dxlnode = |
| (*assert_contraint_list_dxlnode)[ul]; |
| Expr *assert_contraint_expr = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*assert_contraint_dxlnode)[0], &colid_var_mapping); |
| quals_list = gpdb::LAppend(quals_list, assert_contraint_expr); |
| } |
| |
| return quals_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLLimit |
| // |
| // @doc: |
| // Translates a DXL Limit node into a Limit node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLLimit( |
| const CDXLNode *limit_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create limit node |
| Limit *limit = MakeNode(Limit); |
| |
| Plan *plan = &(limit->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(limit_dxlnode, plan); |
| |
| GPOS_ASSERT(4 == limit_dxlnode->Arity()); |
| |
| CDXLTranslateContext left_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| |
| // translate proj list |
| CDXLNode *project_list_dxlnode = (*limit_dxlnode)[EdxllimitIndexProjList]; |
| CDXLNode *child_plan_dxlnode = (*limit_dxlnode)[EdxllimitIndexChildPlan]; |
| CDXLNode *limit_count_dxlnode = (*limit_dxlnode)[EdxllimitIndexLimitCount]; |
| CDXLNode *limit_offset_dxlnode = |
| (*limit_dxlnode)[EdxllimitIndexLimitOffset]; |
| |
| // NOTE: Limit node has only the left plan while the right plan is left empty |
| Plan *left_plan = |
| TranslateDXLOperatorToPlan(child_plan_dxlnode, &left_dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&left_dxl_translate_ctxt); |
| |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| plan->lefttree = left_plan; |
| |
| if (nullptr != limit_count_dxlnode && limit_count_dxlnode->Arity() > 0) |
| { |
| CMappingColIdVarPlStmt colid_var_mapping(m_mp, nullptr, child_contexts, |
| output_context, |
| m_dxl_to_plstmt_context); |
| Node *limit_count = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*limit_count_dxlnode)[0], &colid_var_mapping); |
| limit->limitCount = limit_count; |
| } |
| |
| if (nullptr != limit_offset_dxlnode && limit_offset_dxlnode->Arity() > 0) |
| { |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| Node *limit_offset = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*limit_offset_dxlnode)[0], &colid_var_mapping); |
| limit->limitOffset = limit_offset; |
| } |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) limit; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLHashJoin |
| // |
| // @doc: |
| // Translates a DXL hash join node into a HashJoin node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLHashJoin( |
| const CDXLNode *hj_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| GPOS_ASSERT(hj_dxlnode->GetOperator()->GetDXLOperator() == |
| EdxlopPhysicalHashJoin); |
| GPOS_ASSERT(hj_dxlnode->Arity() == EdxlhjIndexSentinel); |
| |
| // create hash join node |
| HashJoin *hashjoin = MakeNode(HashJoin); |
| |
| Join *join = &(hashjoin->join); |
| Plan *plan = &(join->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalHashJoin *hashjoin_dxlop = |
| CDXLPhysicalHashJoin::Cast(hj_dxlnode->GetOperator()); |
| |
| // set join type |
| join->jointype = |
| GetGPDBJoinTypeFromDXLJoinType(hashjoin_dxlop->GetJoinType()); |
| join->prefetch_inner = true; |
| |
| // translate operator costs |
| TranslatePlanCosts(hj_dxlnode, plan); |
| |
| // translate join children |
| CDXLNode *left_tree_dxlnode = (*hj_dxlnode)[EdxlhjIndexHashLeft]; |
| CDXLNode *right_tree_dxlnode = (*hj_dxlnode)[EdxlhjIndexHashRight]; |
| CDXLNode *project_list_dxlnode = (*hj_dxlnode)[EdxlhjIndexProjList]; |
| CDXLNode *filter_dxlnode = (*hj_dxlnode)[EdxlhjIndexFilter]; |
| CDXLNode *join_filter_dxlnode = (*hj_dxlnode)[EdxlhjIndexJoinFilter]; |
| CDXLNode *hash_cond_list_dxlnode = (*hj_dxlnode)[EdxlhjIndexHashCondList]; |
| |
| CDXLTranslateContext left_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| CDXLTranslateContext right_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| |
| Plan *left_plan = |
| TranslateDXLOperatorToPlan(left_tree_dxlnode, &left_dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| |
| // the right side of the join is the one where the hash phase is done |
| CDXLTranslationContextArray *translation_context_arr_with_siblings = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| translation_context_arr_with_siblings->Append(&left_dxl_translate_ctxt); |
| translation_context_arr_with_siblings->AppendArray( |
| ctxt_translation_prev_siblings); |
| Plan *right_plan = |
| (Plan *) TranslateDXLHash(right_tree_dxlnode, &right_dxl_translate_ctxt, |
| translation_context_arr_with_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&left_dxl_translate_ctxt); |
| child_contexts->Append(&right_dxl_translate_ctxt); |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| // translate join filter |
| join->joinqual = TranslateDXLFilterToQual( |
| join_filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| // translate hash cond |
| List *hash_conditions_list = NIL; |
| |
| BOOL has_is_not_distinct_from_cond = false; |
| |
| const ULONG arity = hash_cond_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *hash_cond_dxlnode = (*hash_cond_list_dxlnode)[ul]; |
| |
| List *hash_cond_list = |
| TranslateDXLScCondToQual(hash_cond_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| GPOS_ASSERT(1 == gpdb::ListLength(hash_cond_list)); |
| |
| Expr *expr = (Expr *) LInitial(hash_cond_list); |
| if (IsA(expr, BoolExpr) && ((BoolExpr *) expr)->boolop == NOT_EXPR) |
| { |
| // INDF test |
| GPOS_ASSERT(gpdb::ListLength(((BoolExpr *) expr)->args) == 1 && |
| (IsA((Expr *) LInitial(((BoolExpr *) expr)->args), |
| DistinctExpr))); |
| has_is_not_distinct_from_cond = true; |
| } |
| hash_conditions_list = |
| gpdb::ListConcat(hash_conditions_list, hash_cond_list); |
| } |
| |
| if (!has_is_not_distinct_from_cond) |
| { |
| // no INDF conditions in the hash condition list |
| hashjoin->hashclauses = hash_conditions_list; |
| } |
| else |
| { |
| // hash conditions contain INDF clauses -> extract equality conditions to |
| // construct the hash clauses list |
| List *hash_clauses_list = NIL; |
| |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *hash_cond_dxlnode = (*hash_cond_list_dxlnode)[ul]; |
| |
| // condition can be either a scalar comparison or a NOT DISTINCT FROM expression |
| GPOS_ASSERT( |
| EdxlopScalarCmp == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator() || |
| EdxlopScalarBoolExpr == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| if (EdxlopScalarBoolExpr == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator()) |
| { |
| // clause is a NOT DISTINCT FROM check -> extract the distinct comparison node |
| GPOS_ASSERT(Edxlnot == CDXLScalarBoolExpr::Cast( |
| hash_cond_dxlnode->GetOperator()) |
| ->GetDxlBoolTypeStr()); |
| hash_cond_dxlnode = (*hash_cond_dxlnode)[0]; |
| GPOS_ASSERT(EdxlopScalarDistinct == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator()); |
| } |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| // translate the DXL scalar or scalar distinct comparison into an equality comparison |
| // to store in the hash clauses |
| Expr *hash_clause_expr = |
| (Expr *) |
| m_translator_dxl_to_scalar->TranslateDXLScalarCmpToScalar( |
| hash_cond_dxlnode, &colid_var_mapping); |
| |
| hash_clauses_list = |
| gpdb::LAppend(hash_clauses_list, hash_clause_expr); |
| } |
| |
| hashjoin->hashclauses = hash_clauses_list; |
| hashjoin->hashqualclauses = hash_conditions_list; |
| } |
| |
| GPOS_ASSERT(NIL != hashjoin->hashclauses); |
| |
| CDXLTranslationContextArray *hash_child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| hash_child_contexts->Append(&left_dxl_translate_ctxt); |
| left_dxl_translate_ctxt.MergeTcxt(&right_dxl_translate_ctxt); |
| hash_child_contexts->Append(&right_dxl_translate_ctxt); |
| |
| List* hashclause_list = NIL; |
| |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *hash_cond_dxlnode = (*hash_cond_list_dxlnode)[ul]; |
| |
| if (EdxlopScalarBoolExpr == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator()) |
| { |
| // clause is a NOT DISTINCT FROM check -> extract the distinct comparison node |
| GPOS_ASSERT(Edxlnot == CDXLScalarBoolExpr::Cast( |
| hash_cond_dxlnode->GetOperator()) |
| ->GetDxlBoolTypeStr()); |
| hash_cond_dxlnode = (*hash_cond_dxlnode)[0]; |
| GPOS_ASSERT(EdxlopScalarDistinct == |
| hash_cond_dxlnode->GetOperator()->GetDXLOperator()); |
| } |
| |
| CMappingColIdVarPlStmt hj_colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, hash_child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| // translate the DXL scalar or scalar distinct comparison into an equality comparison |
| // to store in the hashclause_list |
| Expr *hash_clause_expr = |
| (Expr *) |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| hash_cond_dxlnode, &hj_colid_var_mapping); |
| hashclause_list = |
| gpdb::LAppend(hashclause_list, hash_clause_expr); |
| |
| } |
| |
| List *hashoperators = NIL; |
| List *hashcollations = NIL; |
| List *inner_hashkeys = NIL; |
| List *outer_hashkeys = NIL; |
| ListCell *lc; |
| |
| Hash *hash = (Hash *) right_plan; |
| |
| ForEach(lc, hashclause_list) |
| { |
| Node *clause = (Node *) lfirst(lc); |
| GPOS_ASSERT((IsA(clause, OpExpr) || IsA(clause, DistinctExpr))); |
| OpExpr *hclause = (OpExpr *) clause; |
| |
| hashoperators = gpdb::LAppendOid(hashoperators, hclause->opno); |
| hashcollations = gpdb::LAppendOid(hashcollations, hclause->inputcollid); |
| |
| outer_hashkeys = gpdb::LAppend(outer_hashkeys, linitial(hclause->args)); |
| inner_hashkeys = gpdb::LAppend(inner_hashkeys, lsecond(hclause->args)); |
| } |
| |
| hashjoin->hashoperators = hashoperators; |
| hashjoin->hashcollations = hashcollations; |
| hashjoin->hashkeys = outer_hashkeys; |
| hash->hashkeys = inner_hashkeys; |
| |
| plan->lefttree = left_plan; |
| plan->righttree = right_plan; |
| SetParamIds(plan); |
| |
| // cleanup |
| translation_context_arr_with_siblings->Release(); |
| child_contexts->Release(); |
| hash_child_contexts->Release(); |
| |
| return (Plan *) hashjoin; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLTvf |
| // |
| // @doc: |
| // Translates a DXL TVF node into a GPDB Function scan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLTvf( |
| const CDXLNode *tvf_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray * /*ctxt_translation_prev_siblings*/) |
| { |
| CDXLPhysicalTVF *dxlop = CDXLPhysicalTVF::Cast(tvf_dxlnode->GetOperator()); |
| // translation context for column mappings |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| // create function scan node |
| FunctionScan *func_scan = MakeNode(FunctionScan); |
| Plan *plan = &(func_scan->scan.plan); |
| |
| RangeTblEntry *rte = TranslateDXLTvfToRangeTblEntry( |
| tvf_dxlnode, output_context, &base_table_context); |
| GPOS_ASSERT(rte != nullptr); |
| GPOS_ASSERT(list_length(rte->functions) == 1); |
| RangeTblFunction *rtfunc = |
| (RangeTblFunction *) gpdb::CopyObject(linitial(rte->functions)); |
| |
| // we will add the new range table entry as the last element of the range table |
| Index index = |
| gpdb::ListLength(m_dxl_to_plstmt_context->GetRTableEntriesList()) + 1; |
| base_table_context.SetRelIndex(index); |
| func_scan->scan.scanrelid = index; |
| |
| m_dxl_to_plstmt_context->AddRTE(rte); |
| |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(tvf_dxlnode, plan); |
| |
| // a table scan node must have at least 1 child: projection list |
| GPOS_ASSERT(1 <= tvf_dxlnode->Arity()); |
| |
| CDXLNode *project_list_dxlnode = (*tvf_dxlnode)[EdxltsIndexProjList]; |
| |
| // translate proj list |
| List *target_list = TranslateDXLProjList( |
| project_list_dxlnode, &base_table_context, nullptr, output_context); |
| |
| if (dxlop->FuncMdId()->IsValid()) |
| { |
| target_list = gpdb::ProcessRecordFuncTargetList(CMDIdGPDB::CastMdid(dxlop->FuncMdId())->Oid(), target_list); |
| } |
| plan->targetlist = target_list; |
| |
| ListCell *lc_target_entry = nullptr; |
| |
| rtfunc->funccolnames = NIL; |
| rtfunc->funccoltypes = NIL; |
| rtfunc->funccoltypmods = NIL; |
| rtfunc->funccolcollations = NIL; |
| rtfunc->funccolcount = gpdb::ListLength(target_list); |
| ForEach(lc_target_entry, target_list) |
| { |
| TargetEntry *target_entry = (TargetEntry *) lfirst(lc_target_entry); |
| OID oid_type = gpdb::ExprType((Node *) target_entry->expr); |
| GPOS_ASSERT(InvalidOid != oid_type); |
| |
| INT typ_mod = gpdb::ExprTypeMod((Node *) target_entry->expr); |
| Oid collation_type_oid = gpdb::TypeCollation(oid_type); |
| |
| rtfunc->funccolnames = gpdb::LAppend( |
| rtfunc->funccolnames, gpdb::MakeStringValue(target_entry->resname)); |
| rtfunc->funccoltypes = gpdb::LAppendOid(rtfunc->funccoltypes, oid_type); |
| rtfunc->funccoltypmods = |
| gpdb::LAppendInt(rtfunc->funccoltypmods, typ_mod); |
| // GPDB_91_MERGE_FIXME: collation |
| rtfunc->funccolcollations = |
| gpdb::LAppendOid(rtfunc->funccolcollations, collation_type_oid); |
| } |
| func_scan->functions = ListMake1(rtfunc); |
| |
| SetParamIds(plan); |
| |
| return (Plan *) func_scan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLTvfToRangeTblEntry |
| // |
| // @doc: |
| // Create a range table entry from a CDXLPhysicalTVF node |
| // |
| //--------------------------------------------------------------------------- |
| RangeTblEntry * |
| CTranslatorDXLToPlStmt::TranslateDXLTvfToRangeTblEntry( |
| const CDXLNode *tvf_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslateContextBaseTable *base_table_context) |
| { |
| CDXLPhysicalTVF *dxlop = CDXLPhysicalTVF::Cast(tvf_dxlnode->GetOperator()); |
| |
| RangeTblEntry *rte = MakeNode(RangeTblEntry); |
| rte->rtekind = RTE_FUNCTION; |
| |
| // get function alias |
| Alias *alias = MakeNode(Alias); |
| alias->colnames = NIL; |
| alias->aliasname = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| dxlop->Pstr()->GetBuffer()); |
| |
| // project list |
| CDXLNode *project_list_dxlnode = (*tvf_dxlnode)[EdxltsIndexProjList]; |
| |
| // get column names |
| const ULONG num_of_cols = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < num_of_cols; ul++) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| CDXLScalarProjElem *dxl_proj_elem = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| |
| CHAR *col_name_char_array = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| dxl_proj_elem->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| |
| Value *val_colname = gpdb::MakeStringValue(col_name_char_array); |
| alias->colnames = gpdb::LAppend(alias->colnames, val_colname); |
| |
| // save mapping col id -> index in translate context |
| (void) base_table_context->InsertMapping(dxl_proj_elem->Id(), |
| ul + 1 /*attno*/); |
| } |
| |
| RangeTblFunction *rtfunc = MakeNode(RangeTblFunction); |
| Bitmapset *funcparams = nullptr; |
| |
| // invalid funcid indicates TVF evaluates to const |
| if (!dxlop->FuncMdId()->IsValid()) |
| { |
| Const *const_expr = MakeNode(Const); |
| |
| const_expr->consttype = |
| CMDIdGPDB::CastMdid(dxlop->ReturnTypeMdId())->Oid(); |
| const_expr->consttypmod = -1; |
| |
| CDXLNode *constVa = (*tvf_dxlnode)[1]; |
| CDXLScalarConstValue *constValue = |
| CDXLScalarConstValue::Cast(constVa->GetOperator()); |
| const CDXLDatum *datum_dxl = constValue->GetDatumVal(); |
| CDXLDatumGeneric *datum_generic_dxl = |
| CDXLDatumGeneric::Cast(const_cast<gpdxl::CDXLDatum *>(datum_dxl)); |
| const IMDType *type = |
| m_md_accessor->RetrieveType(datum_generic_dxl->MDId()); |
| const_expr->constlen = type->Length(); |
| Datum val = gpdb::DatumFromPointer(datum_generic_dxl->GetByteArray()); |
| ULONG length = |
| (ULONG) gpdb::DatumSize(val, false, const_expr->constlen); |
| CHAR *str = (CHAR *) gpdb::GPDBAlloc(length + 1); |
| memcpy(str, datum_generic_dxl->GetByteArray(), length); |
| str[length] = '\0'; |
| const_expr->constvalue = gpdb::DatumFromPointer(str); |
| |
| rtfunc->funcexpr = (Node *) const_expr; |
| } |
| else |
| { |
| FuncExpr *func_expr = MakeNode(FuncExpr); |
| |
| func_expr->funcid = CMDIdGPDB::CastMdid(dxlop->FuncMdId())->Oid(); |
| func_expr->funcretset = gpdb::GetFuncRetset(func_expr->funcid); |
| // this is a function call, as opposed to a cast |
| func_expr->funcformat = COERCE_EXPLICIT_CALL; |
| func_expr->funcresulttype = |
| CMDIdGPDB::CastMdid(dxlop->ReturnTypeMdId())->Oid(); |
| |
| // function arguments |
| const ULONG num_of_child = tvf_dxlnode->Arity(); |
| for (ULONG ul = 1; ul < num_of_child; ++ul) |
| { |
| CDXLNode *func_arg_dxlnode = (*tvf_dxlnode)[ul]; |
| |
| CMappingColIdVarPlStmt colid_var_mapping(m_mp, base_table_context, |
| nullptr, output_context, |
| m_dxl_to_plstmt_context); |
| |
| Expr *pexprFuncArg = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| func_arg_dxlnode, &colid_var_mapping); |
| func_expr->args = gpdb::LAppend(func_expr->args, pexprFuncArg); |
| } |
| |
| // GPDB_91_MERGE_FIXME: collation |
| func_expr->inputcollid = gpdb::ExprCollation((Node *) func_expr->args); |
| func_expr->funccollid = gpdb::TypeCollation(func_expr->funcresulttype); |
| |
| // Populate RangeTblFunction::funcparams, by walking down the entire |
| // func_expr to capture ids of all the PARAMs |
| ListCell *lc = nullptr; |
| List *param_exprs = gpdb::ExtractNodesExpression( |
| (Node *) func_expr, T_Param, false /*descend_into_subqueries */); |
| ForEach(lc, param_exprs) |
| { |
| Param *param = (Param *) lfirst(lc); |
| funcparams = gpdb::BmsAddMember(funcparams, param->paramid); |
| } |
| |
| rtfunc->funcexpr = (Node *) func_expr; |
| } |
| |
| rtfunc->funccolcount = (int) num_of_cols; |
| rtfunc->funcparams = funcparams; |
| // GPDB_91_MERGE_FIXME: collation |
| // set rtfunc->funccoltypemods & rtfunc->funccolcollations? |
| rte->functions = ListMake1(rtfunc); |
| |
| rte->inFromCl = true; |
| |
| rte->eref = alias; |
| return rte; |
| } |
| |
| |
| // create a range table entry from a CDXLPhysicalValuesScan node |
| RangeTblEntry * |
| CTranslatorDXLToPlStmt::TranslateDXLValueScanToRangeTblEntry( |
| const CDXLNode *value_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslateContextBaseTable *base_table_context) |
| { |
| CDXLPhysicalValuesScan *phy_values_scan_dxlop = |
| CDXLPhysicalValuesScan::Cast(value_scan_dxlnode->GetOperator()); |
| |
| RangeTblEntry *rte = MakeNode(RangeTblEntry); |
| |
| rte->relid = InvalidOid; |
| rte->subquery = nullptr; |
| rte->rtekind = RTE_VALUES; |
| rte->inh = false; /* never true for values RTEs */ |
| rte->inFromCl = true; |
| rte->requiredPerms = 0; |
| rte->checkAsUser = InvalidOid; |
| |
| Alias *alias = MakeNode(Alias); |
| alias->colnames = NIL; |
| |
| // get value alias |
| alias->aliasname = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| phy_values_scan_dxlop->GetOpNameStr()->GetBuffer()); |
| |
| // project list |
| CDXLNode *project_list_dxlnode = (*value_scan_dxlnode)[EdxltsIndexProjList]; |
| |
| // get column names |
| const ULONG num_of_cols = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < num_of_cols; ul++) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| CDXLScalarProjElem *dxl_proj_elem = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| |
| CHAR *col_name_char_array = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| dxl_proj_elem->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| |
| Value *val_colname = gpdb::MakeStringValue(col_name_char_array); |
| alias->colnames = gpdb::LAppend(alias->colnames, val_colname); |
| |
| // save mapping col id -> index in translate context |
| (void) base_table_context->InsertMapping(dxl_proj_elem->Id(), |
| ul + 1 /*attno*/); |
| } |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, base_table_context, nullptr, |
| output_context, m_dxl_to_plstmt_context); |
| const ULONG num_of_child = value_scan_dxlnode->Arity(); |
| List *values_lists = NIL; |
| List *values_collations = NIL; |
| |
| for (ULONG ulValue = EdxlValIndexConstStart; ulValue < num_of_child; |
| ulValue++) |
| { |
| CDXLNode *value_list_dxlnode = (*value_scan_dxlnode)[ulValue]; |
| const ULONG num_of_cols = value_list_dxlnode->Arity(); |
| List *value = NIL; |
| for (ULONG ulCol = 0; ulCol < num_of_cols; ulCol++) |
| { |
| Expr *const_expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*value_list_dxlnode)[ulCol], &colid_var_mapping); |
| value = gpdb::LAppend(value, const_expr); |
| } |
| values_lists = gpdb::LAppend(values_lists, value); |
| |
| // GPDB_91_MERGE_FIXME: collation |
| if (NIL == values_collations) |
| { |
| // Set collation based on the first list of values |
| for (ULONG ulCol = 0; ulCol < num_of_cols; ulCol++) |
| { |
| values_collations = gpdb::LAppendOid( |
| values_collations, gpdb::ExprCollation((Node *) value)); |
| } |
| } |
| } |
| |
| rte->values_lists = values_lists; |
| rte->colcollations = values_collations; |
| rte->eref = alias; |
| |
| return rte; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLNLJoin |
| // |
| // @doc: |
| // Translates a DXL nested loop join node into a NestLoop plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLNLJoin( |
| const CDXLNode *nl_join_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| GPOS_ASSERT(nl_join_dxlnode->GetOperator()->GetDXLOperator() == |
| EdxlopPhysicalNLJoin); |
| GPOS_ASSERT(nl_join_dxlnode->Arity() == EdxlnljIndexSentinel); |
| |
| // create hash join node |
| NestLoop *nested_loop = MakeNode(NestLoop); |
| |
| Join *join = &(nested_loop->join); |
| Plan *plan = &(join->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalNLJoin *dxl_nlj = |
| CDXLPhysicalNLJoin::PdxlConvert(nl_join_dxlnode->GetOperator()); |
| |
| // set join type |
| join->jointype = GetGPDBJoinTypeFromDXLJoinType(dxl_nlj->GetJoinType()); |
| |
| // translate operator costs |
| TranslatePlanCosts(nl_join_dxlnode, plan); |
| |
| // translate join children |
| CDXLNode *left_tree_dxlnode = (*nl_join_dxlnode)[EdxlnljIndexLeftChild]; |
| CDXLNode *right_tree_dxlnode = (*nl_join_dxlnode)[EdxlnljIndexRightChild]; |
| |
| CDXLNode *project_list_dxlnode = (*nl_join_dxlnode)[EdxlnljIndexProjList]; |
| CDXLNode *filter_dxlnode = (*nl_join_dxlnode)[EdxlnljIndexFilter]; |
| CDXLNode *join_filter_dxlnode = (*nl_join_dxlnode)[EdxlnljIndexJoinFilter]; |
| |
| CDXLTranslateContext left_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| CDXLTranslateContext right_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| |
| // setting of prefetch_inner to true except for the case of index NLJ where we cannot prefetch inner |
| // because inner child depends on variables coming from outer child |
| join->prefetch_inner = !dxl_nlj->IsIndexNLJ(); |
| |
| CDXLTranslationContextArray *translation_context_arr_with_siblings = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| Plan *left_plan = nullptr; |
| Plan *right_plan = nullptr; |
| if (dxl_nlj->IsIndexNLJ()) |
| { |
| const CDXLColRefArray *pdrgdxlcrOuterRefs = |
| dxl_nlj->GetNestLoopParamsColRefs(); |
| const ULONG ulLen = pdrgdxlcrOuterRefs->Size(); |
| for (ULONG ul = 0; ul < ulLen; ul++) |
| { |
| CDXLColRef *pdxlcr = (*pdrgdxlcrOuterRefs)[ul]; |
| IMDId *pmdid = pdxlcr->MdidType(); |
| ULONG ulColid = pdxlcr->Id(); |
| INT iTypeModifier = pdxlcr->TypeModifier(); |
| OID iTypeOid = CMDIdGPDB::CastMdid(pmdid)->Oid(); |
| |
| if (nullptr == |
| right_dxl_translate_ctxt.GetParamIdMappingElement(ulColid)) |
| { |
| ULONG param_id = |
| m_dxl_to_plstmt_context->GetNextParamId(iTypeOid); |
| CMappingElementColIdParamId *pmecolidparamid = |
| GPOS_NEW(m_mp) CMappingElementColIdParamId( |
| ulColid, param_id, pmdid, iTypeModifier); |
| #ifdef GPOS_DEBUG |
| BOOL fInserted GPOS_ASSERTS_ONLY = |
| #endif |
| right_dxl_translate_ctxt.FInsertParamMapping( |
| ulColid, pmecolidparamid); |
| GPOS_ASSERT(fInserted); |
| } |
| } |
| // right child (the index scan side) has references to left child's columns, |
| // we need to translate left child first to load its columns into translation context |
| left_plan = TranslateDXLOperatorToPlan(left_tree_dxlnode, |
| &left_dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| |
| translation_context_arr_with_siblings->Append(&left_dxl_translate_ctxt); |
| translation_context_arr_with_siblings->AppendArray( |
| ctxt_translation_prev_siblings); |
| |
| // translate right child after left child translation is complete |
| right_plan = TranslateDXLOperatorToPlan( |
| right_tree_dxlnode, &right_dxl_translate_ctxt, |
| translation_context_arr_with_siblings); |
| } |
| else |
| { |
| // left child may include a PartitionSelector with references to right child's columns, |
| // we need to translate right child first to load its columns into translation context |
| right_plan = TranslateDXLOperatorToPlan(right_tree_dxlnode, |
| &right_dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| |
| translation_context_arr_with_siblings->Append( |
| &right_dxl_translate_ctxt); |
| translation_context_arr_with_siblings->AppendArray( |
| ctxt_translation_prev_siblings); |
| |
| // translate left child after right child translation is complete |
| left_plan = TranslateDXLOperatorToPlan( |
| left_tree_dxlnode, &left_dxl_translate_ctxt, |
| translation_context_arr_with_siblings); |
| } |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&left_dxl_translate_ctxt); |
| child_contexts->Append(&right_dxl_translate_ctxt); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| // translate join condition |
| join->joinqual = TranslateDXLFilterToQual( |
| join_filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| // create nest loop params for index nested loop joins |
| if (dxl_nlj->IsIndexNLJ()) |
| { |
| ((NestLoop *) plan)->nestParams = TranslateNestLoopParamList( |
| dxl_nlj->GetNestLoopParamsColRefs(), &left_dxl_translate_ctxt, |
| &right_dxl_translate_ctxt); |
| } |
| plan->lefttree = left_plan; |
| plan->righttree = right_plan; |
| SetParamIds(plan); |
| |
| // cleanup |
| translation_context_arr_with_siblings->Release(); |
| child_contexts->Release(); |
| |
| return (Plan *) nested_loop; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLMergeJoin |
| // |
| // @doc: |
| // Translates a DXL merge join node into a MergeJoin node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLMergeJoin( |
| const CDXLNode *merge_join_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| GPOS_ASSERT(merge_join_dxlnode->GetOperator()->GetDXLOperator() == |
| EdxlopPhysicalMergeJoin); |
| GPOS_ASSERT(merge_join_dxlnode->Arity() == EdxlmjIndexSentinel); |
| |
| // create merge join node |
| MergeJoin *merge_join = MakeNode(MergeJoin); |
| |
| Join *join = &(merge_join->join); |
| Plan *plan = &(join->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalMergeJoin *merge_join_dxlop = |
| CDXLPhysicalMergeJoin::Cast(merge_join_dxlnode->GetOperator()); |
| |
| // set join type |
| join->jointype = |
| GetGPDBJoinTypeFromDXLJoinType(merge_join_dxlop->GetJoinType()); |
| |
| // translate operator costs |
| TranslatePlanCosts(merge_join_dxlnode, plan); |
| |
| // translate join children |
| CDXLNode *left_tree_dxlnode = (*merge_join_dxlnode)[EdxlmjIndexLeftChild]; |
| CDXLNode *right_tree_dxlnode = (*merge_join_dxlnode)[EdxlmjIndexRightChild]; |
| |
| CDXLNode *project_list_dxlnode = (*merge_join_dxlnode)[EdxlmjIndexProjList]; |
| CDXLNode *filter_dxlnode = (*merge_join_dxlnode)[EdxlmjIndexFilter]; |
| CDXLNode *join_filter_dxlnode = |
| (*merge_join_dxlnode)[EdxlmjIndexJoinFilter]; |
| CDXLNode *merge_cond_list_dxlnode = |
| (*merge_join_dxlnode)[EdxlmjIndexMergeCondList]; |
| |
| CDXLTranslateContext left_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| CDXLTranslateContext right_dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| |
| Plan *left_plan = |
| TranslateDXLOperatorToPlan(left_tree_dxlnode, &left_dxl_translate_ctxt, |
| ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *translation_context_arr_with_siblings = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| translation_context_arr_with_siblings->Append(&left_dxl_translate_ctxt); |
| translation_context_arr_with_siblings->AppendArray( |
| ctxt_translation_prev_siblings); |
| |
| Plan *right_plan = TranslateDXLOperatorToPlan( |
| right_tree_dxlnode, &right_dxl_translate_ctxt, |
| translation_context_arr_with_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&left_dxl_translate_ctxt); |
| child_contexts->Append(&right_dxl_translate_ctxt); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| // translate join filter |
| join->joinqual = TranslateDXLFilterToQual( |
| join_filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| // translate merge cond |
| List *merge_conditions_list = NIL; |
| |
| const ULONG num_join_conds = merge_cond_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < num_join_conds; ul++) |
| { |
| CDXLNode *merge_condition_dxlnode = (*merge_cond_list_dxlnode)[ul]; |
| List *merge_condition_list = |
| TranslateDXLScCondToQual(merge_condition_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| GPOS_ASSERT(1 == gpdb::ListLength(merge_condition_list)); |
| merge_conditions_list = |
| gpdb::ListConcat(merge_conditions_list, merge_condition_list); |
| } |
| |
| GPOS_ASSERT(NIL != merge_conditions_list); |
| |
| merge_join->mergeclauses = merge_conditions_list; |
| |
| plan->lefttree = left_plan; |
| plan->righttree = right_plan; |
| SetParamIds(plan); |
| |
| merge_join->mergeFamilies = |
| (Oid *) gpdb::GPDBAlloc(sizeof(Oid) * num_join_conds); |
| merge_join->mergeStrategies = |
| (int *) gpdb::GPDBAlloc(sizeof(int) * num_join_conds); |
| merge_join->mergeCollations = |
| (Oid *) gpdb::GPDBAlloc(sizeof(Oid) * num_join_conds); |
| merge_join->mergeNullsFirst = |
| (bool *) gpdb::GPDBAlloc(sizeof(bool) * num_join_conds); |
| |
| ListCell *lc; |
| ULONG ul = 0; |
| foreach (lc, merge_join->mergeclauses) |
| { |
| Expr *expr = (Expr *) lfirst(lc); |
| |
| if (IsA(expr, OpExpr)) |
| { |
| // we are ok - phew |
| OpExpr *opexpr = (OpExpr *) expr; |
| List *mergefamilies = gpdb::GetMergeJoinOpFamilies(opexpr->opno); |
| |
| GPOS_ASSERT(nullptr != mergefamilies && |
| gpdb::ListLength(mergefamilies) > 0); |
| |
| // Pick the first - it's probably what we want |
| merge_join->mergeFamilies[ul] = gpdb::ListNthOid(mergefamilies, 0); |
| |
| GPOS_ASSERT(gpdb::ListLength(opexpr->args) == 2); |
| Expr *leftarg = (Expr *) gpdb::ListNth(opexpr->args, 0); |
| |
| Expr *rightarg PG_USED_FOR_ASSERTS_ONLY = |
| (Expr *) gpdb::ListNth(opexpr->args, 1); |
| GPOS_ASSERT(gpdb::ExprCollation((Node *) leftarg) == |
| gpdb::ExprCollation((Node *) rightarg)); |
| |
| merge_join->mergeCollations[ul] = |
| gpdb::ExprCollation((Node *) leftarg); |
| |
| // Make sure that the following properties match |
| // those in CPhysicalFullMergeJoin::PosRequired(). |
| merge_join->mergeStrategies[ul] = BTLessStrategyNumber; |
| merge_join->mergeNullsFirst[ul] = false; |
| ++ul; |
| } |
| else |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, |
| GPOS_WSZ_LIT("Not an op expression in merge clause")); |
| break; |
| } |
| } |
| |
| // cleanup |
| translation_context_arr_with_siblings->Release(); |
| child_contexts->Release(); |
| |
| return (Plan *) merge_join; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLHash |
| // |
| // @doc: |
| // Translates a DXL physical operator node into a Hash node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLHash( |
| const CDXLNode *dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| Hash *hash = MakeNode(Hash); |
| |
| Plan *plan = &(hash->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate dxl node |
| CDXLTranslateContext dxl_translate_ctxt( |
| m_mp, false, output_context->GetColIdToParamIdMap()); |
| |
| Plan *left_plan = TranslateDXLOperatorToPlan( |
| dxlnode, &dxl_translate_ctxt, ctxt_translation_prev_siblings); |
| |
| GPOS_ASSERT(0 < dxlnode->Arity()); |
| |
| // create a reference to each entry in the child project list to create the target list of |
| // the hash node |
| CDXLNode *project_list_dxlnode = (*dxlnode)[0]; |
| List *target_list = TranslateDXLProjectListToHashTargetList( |
| project_list_dxlnode, &dxl_translate_ctxt, output_context); |
| |
| // copy costs from child node; the startup cost for the hash node is the total cost |
| // of the child plan, see make_hash in createplan.c |
| plan->startup_cost = left_plan->total_cost; |
| plan->total_cost = left_plan->total_cost; |
| plan->plan_rows = left_plan->plan_rows; |
| plan->plan_width = left_plan->plan_width; |
| |
| plan->targetlist = target_list; |
| plan->lefttree = left_plan; |
| plan->righttree = nullptr; |
| plan->qual = NIL; |
| hash->rescannable = false; |
| |
| SetParamIds(plan); |
| |
| return (Plan *) hash; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDuplicateSensitiveMotion |
| // |
| // @doc: |
| // Translate DXL motion node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLDuplicateSensitiveMotion( |
| const CDXLNode *motion_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalMotion *motion_dxlop = |
| CDXLPhysicalMotion::Cast(motion_dxlnode->GetOperator()); |
| if (CTranslatorUtils::IsDuplicateSensitiveMotion(motion_dxlop)) |
| { |
| return TranslateDXLRedistributeMotionToResultHashFilters( |
| motion_dxlnode, output_context, ctxt_translation_prev_siblings); |
| } |
| |
| return TranslateDXLMotion(motion_dxlnode, output_context, |
| ctxt_translation_prev_siblings); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLMotion |
| // |
| // @doc: |
| // Translate DXL motion node into GPDB Motion plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLMotion( |
| const CDXLNode *motion_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalMotion *motion_dxlop = |
| CDXLPhysicalMotion::Cast(motion_dxlnode->GetOperator()); |
| const IntPtrArray *input_segids_array = motion_dxlop->GetInputSegIdsArray(); |
| PlanSlice *recvslice = m_dxl_to_plstmt_context->GetCurrentSlice(); |
| |
| // create motion node |
| Motion *motion = MakeNode(Motion); |
| |
| Plan *plan = &(motion->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // Translate operator costs before changing the current slice. |
| TranslatePlanCosts(motion_dxlnode, plan); |
| |
| CDXLNode *project_list_dxlnode = (*motion_dxlnode)[EdxlgmIndexProjList]; |
| CDXLNode *filter_dxlnode = (*motion_dxlnode)[EdxlgmIndexFilter]; |
| CDXLNode *sort_col_list_dxl = (*motion_dxlnode)[EdxlgmIndexSortColList]; |
| |
| PlanSlice *sendslice = (PlanSlice *) gpdb::GPDBAlloc(sizeof(PlanSlice)); |
| memset(sendslice, 0, sizeof(PlanSlice)); |
| |
| sendslice->sliceIndex = m_dxl_to_plstmt_context->AddSlice(sendslice); |
| sendslice->parentIndex = recvslice->sliceIndex; |
| m_dxl_to_plstmt_context->SetCurrentSlice(sendslice); |
| |
| // only one sender |
| if (1 == input_segids_array->Size()) |
| { |
| int segindex = *((*input_segids_array)[0]); |
| |
| // only one segment in total |
| if (segindex == MASTER_CONTENT_ID) |
| { |
| // sender is on master, must be singleton gang |
| sendslice->gangType = GANGTYPE_ENTRYDB_READER; |
| } |
| else if (1 == gpdb::GetGPSegmentCount()) |
| { |
| // sender is on segment, can not tell it's singleton or |
| // all-segment gang, so treat it as all-segment reader gang. |
| // It can be promoted to writer gang later if needed. |
| sendslice->gangType = GANGTYPE_PRIMARY_READER; |
| } |
| else |
| { |
| // multiple segments, must be singleton gang |
| sendslice->gangType = GANGTYPE_SINGLETON_READER; |
| } |
| sendslice->numsegments = 1; |
| sendslice->segindex = segindex; |
| } |
| else |
| { |
| // Mark it as reader for now. Will be overwritten into WRITER, if we |
| // encounter a DML node. |
| sendslice->gangType = GANGTYPE_PRIMARY_READER; |
| sendslice->numsegments = m_num_of_segments; |
| sendslice->segindex = 0; |
| } |
| sendslice->directDispatch.isDirectDispatch = false; |
| sendslice->directDispatch.contentIds = NIL; |
| sendslice->directDispatch.haveProcessedAnyCalculations = false; |
| |
| // set parallel workers if needed |
| ULONG child_index = motion_dxlop->GetRelationChildIdx(); |
| CDXLNode *child_dxlnode = (*motion_dxlnode)[child_index]; |
| ULONG child_parallel_workers = ExtractParallelWorkersFromDXL(child_dxlnode); |
| if (child_parallel_workers > 1) |
| { |
| // Determine parallel workers based on enable_parallel and gang type |
| bool supports_parallel = (sendslice->gangType == GANGTYPE_PRIMARY_READER || |
| sendslice->gangType == GANGTYPE_PRIMARY_WRITER); |
| |
| if (supports_parallel) |
| { |
| sendslice->parallel_workers = child_parallel_workers; |
| } |
| else |
| { |
| // Disable parallel for: non-PRIMARY gang types |
| // (SINGLETON_READER, ENTRYDB_READER, UNALLOCATED) |
| sendslice->parallel_workers = 0; |
| } |
| } |
| |
| motion->motionID = sendslice->sliceIndex; |
| |
| // translate motion child |
| // child node is in the same position in broadcast and gather motion nodes |
| // but different in redistribute motion nodes |
| // Note: child_index and child_dxlnode already defined above |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| // Recurse into the child, which runs in the sending slice. |
| m_dxl_to_plstmt_context->SetCurrentSlice(sendslice); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| // translate sorting info |
| ULONG num_sort_cols = sort_col_list_dxl->Arity(); |
| if (0 < num_sort_cols) |
| { |
| motion->sendSorted = true; |
| motion->numSortCols = num_sort_cols; |
| motion->sortColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(num_sort_cols * sizeof(AttrNumber)); |
| motion->sortOperators = |
| (Oid *) gpdb::GPDBAlloc(num_sort_cols * sizeof(Oid)); |
| motion->collations = |
| (Oid *) gpdb::GPDBAlloc(num_sort_cols * sizeof(Oid)); |
| motion->nullsFirst = |
| (bool *) gpdb::GPDBAlloc(num_sort_cols * sizeof(bool)); |
| |
| TranslateSortCols(sort_col_list_dxl, output_context, motion->sortColIdx, |
| motion->sortOperators, motion->collations, |
| motion->nullsFirst); |
| } |
| else |
| { |
| // not a sorting motion |
| motion->sendSorted = false; |
| motion->numSortCols = 0; |
| motion->sortColIdx = nullptr; |
| motion->sortOperators = nullptr; |
| motion->nullsFirst = nullptr; |
| } |
| |
| if (motion_dxlop->GetDXLOperator() == EdxlopPhysicalMotionRedistribute || |
| motion_dxlop->GetDXLOperator() == |
| EdxlopPhysicalMotionRoutedDistribute || |
| motion_dxlop->GetDXLOperator() == EdxlopPhysicalMotionRandom) |
| { |
| // translate hash expr list |
| List *hash_expr_list = NIL; |
| List *hash_expr_opfamilies = NIL; |
| int numHashExprs; |
| |
| if (EdxlopPhysicalMotionRedistribute == motion_dxlop->GetDXLOperator()) |
| { |
| CDXLNode *hash_expr_list_dxlnode = |
| (*motion_dxlnode)[EdxlrmIndexHashExprList]; |
| |
| TranslateHashExprList(hash_expr_list_dxlnode, &child_context, |
| &hash_expr_list, &hash_expr_opfamilies, |
| output_context); |
| } |
| numHashExprs = gpdb::ListLength(hash_expr_list); |
| |
| int i = 0; |
| ListCell *lc, *lcoid; |
| Oid *hashFuncs = (Oid *) gpdb::GPDBAlloc(numHashExprs * sizeof(Oid)); |
| |
| if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution)) |
| { |
| GPOS_ASSERT(gpdb::ListLength(hash_expr_list) == |
| gpdb::ListLength(hash_expr_opfamilies)); |
| forboth(lc, hash_expr_list, lcoid, hash_expr_opfamilies) |
| { |
| Node *expr = (Node *) lfirst(lc); |
| Oid typeoid = gpdb::ExprType(expr); |
| Oid opfamily = lfirst_oid(lcoid); |
| hashFuncs[i] = gpdb::GetHashProcInOpfamily(opfamily, typeoid); |
| i++; |
| } |
| } |
| else |
| { |
| foreach (lc, hash_expr_list) |
| { |
| Node *expr = (Node *) lfirst(lc); |
| Oid typeoid = gpdb::ExprType(expr); |
| hashFuncs[i] = |
| m_dxl_to_plstmt_context->GetDistributionHashFuncForType( |
| typeoid); |
| i++; |
| } |
| } |
| |
| |
| motion->hashExprs = hash_expr_list; |
| motion->hashFuncs = hashFuncs; |
| } |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| m_dxl_to_plstmt_context->SetCurrentSlice(recvslice); |
| |
| plan->lefttree = child_plan; |
| |
| // translate properties of the specific type of motion operator |
| |
| switch (motion_dxlop->GetDXLOperator()) |
| { |
| case EdxlopPhysicalMotionGather: |
| { |
| motion->motionType = MOTIONTYPE_GATHER; |
| break; |
| } |
| case EdxlopPhysicalMotionRedistribute: |
| case EdxlopPhysicalMotionRandom: |
| { |
| motion->motionType = MOTIONTYPE_HASH; |
| motion->numHashSegments = |
| (int) motion_dxlop->GetOutputSegIdsArray()->Size(); |
| GPOS_ASSERT(motion->numHashSegments > 0); |
| break; |
| } |
| case EdxlopPhysicalMotionBroadcast: |
| { |
| motion->motionType = MOTIONTYPE_BROADCAST; |
| break; |
| } |
| case EdxlopPhysicalMotionRoutedDistribute: |
| { |
| ULONG segid_col = |
| CDXLPhysicalRoutedDistributeMotion::Cast(motion_dxlop) |
| ->SegmentIdCol(); |
| const TargetEntry *te_sort_col = |
| child_context.GetTargetEntry(segid_col); |
| |
| motion->motionType = MOTIONTYPE_EXPLICIT; |
| motion->segidColIdx = te_sort_col->resno; |
| break; |
| } |
| default: |
| GPOS_ASSERT(!"Unrecognized Motion operator"); |
| return nullptr; |
| } |
| |
| // Adjust row count for parallel execution in the sending slice |
| // The Motion node receives rows from all parallel workers, so we need to |
| // account for the fact that each worker processes a fraction of the rows. |
| // TranslatePlanCosts() already divided by numsegments, but if we have |
| // parallel workers, each segment is further subdivided among workers. |
| if (sendslice->parallel_workers > 1) |
| { |
| plan->plan_rows = ceil(plan->plan_rows / sendslice->parallel_workers); |
| } |
| |
| SetParamIds(plan); |
| |
| return (Plan *) motion; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLRedistributeMotionToResultHashFilters |
| // |
| // @doc: |
| // Translate DXL duplicate sensitive redistribute motion node into |
| // GPDB result node with hash filters |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLRedistributeMotionToResultHashFilters( |
| const CDXLNode *motion_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create motion node |
| Result *result = MakeNode(Result); |
| |
| Plan *plan = &(result->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalMotion *motion_dxlop = |
| CDXLPhysicalMotion::Cast(motion_dxlnode->GetOperator()); |
| |
| // translate operator costs |
| TranslatePlanCosts(motion_dxlnode, plan); |
| |
| CDXLNode *project_list_dxlnode = (*motion_dxlnode)[EdxlrmIndexProjList]; |
| CDXLNode *filter_dxlnode = (*motion_dxlnode)[EdxlrmIndexFilter]; |
| CDXLNode *child_dxlnode = |
| (*motion_dxlnode)[motion_dxlop->GetRelationChildIdx()]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| bool targetlist_modified = false; |
| |
| // translate hash expr list |
| if (EdxlopPhysicalMotionRedistribute == motion_dxlop->GetDXLOperator()) |
| { |
| CDXLNode *hash_expr_list_dxlnode = |
| (*motion_dxlnode)[EdxlrmIndexHashExprList]; |
| const ULONG length = hash_expr_list_dxlnode->Arity(); |
| GPOS_ASSERT(0 < length); |
| |
| result->numHashFilterCols = length; |
| result->hashFilterColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(length * sizeof(AttrNumber)); |
| result->hashFilterFuncs = (Oid *) gpdb::GPDBAlloc(length * sizeof(Oid)); |
| |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CDXLNode *hash_expr_dxlnode = (*hash_expr_list_dxlnode)[ul]; |
| CDXLNode *expr_dxlnode = (*hash_expr_dxlnode)[0]; |
| const TargetEntry *target_entry; |
| |
| if (EdxlopScalarIdent == |
| expr_dxlnode->GetOperator()->GetDXLOperator()) |
| { |
| ULONG colid = CDXLScalarIdent::Cast(expr_dxlnode->GetOperator()) |
| ->GetDXLColRef() |
| ->Id(); |
| target_entry = output_context->GetTargetEntry(colid); |
| } |
| else |
| { |
| // The expression is not a scalar ident that points to an output column in the child node. |
| // Rather, it is an expresssion that is evaluated by the hash filter such as CAST(a) or a+b. |
| // We therefore, create a corresponding GPDB scalar expression and add it to the project list |
| // of the hash filter |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt( |
| m_mp, |
| nullptr, // translate context for the base table |
| child_contexts, output_context, |
| m_dxl_to_plstmt_context); |
| |
| Expr *expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| expr_dxlnode, &colid_var_mapping); |
| GPOS_ASSERT(nullptr != expr); |
| |
| // create a target entry for the hash filter |
| CWStringConst str_unnamed_col(GPOS_WSZ_LIT("?column?")); |
| target_entry = gpdb::MakeTargetEntry( |
| expr, gpdb::ListLength(plan->targetlist) + 1, |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| str_unnamed_col.GetBuffer()), |
| false /* resjunk */); |
| plan->targetlist = |
| gpdb::LAppend(plan->targetlist, (void *) target_entry); |
| targetlist_modified = true; |
| } |
| |
| result->hashFilterColIdx[ul] = target_entry->resno; |
| result->hashFilterFuncs[ul] = |
| m_dxl_to_plstmt_context->GetDistributionHashFuncForType( |
| gpdb::ExprType((Node *) target_entry->expr)); |
| } |
| } |
| else |
| { |
| // A Redistribute Motion without any expressions to hash, means that |
| // the subtree should run on one segment only, and we don't care which |
| // segment it is. That is represented by a One-Off Filter, where we |
| // check that the segment number matches an arbitrarily chosen one. |
| int segment = gpdb::CdbHashRandomSeg(gpdb::GetGPSegmentCount()); |
| |
| result->resconstantqual = |
| (Node *) ListMake1(gpdb::MakeSegmentFilterExpr(segment)); |
| } |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| plan->lefttree = child_plan; |
| |
| SetParamIds(plan); |
| |
| Plan *child_result = (Plan *) result; |
| |
| if (targetlist_modified) |
| { |
| // If the targetlist is modified by adding any expressions, such as for |
| // hashFilterColIdx & hashFilterFuncs, add an additional Result node on top |
| // to project only the elements from the original targetlist. |
| // This is needed in case the Result node is created under the Hash |
| // operator (or any non-projecting node), which expects the targetlist of its |
| // child node to contain only elements that are to be hashed. |
| // We should not generate a plan where the target list of a non-projecting |
| // node such as Hash does not match its child. Additional expressions |
| // here can cause issues with memtuple bindings that can lead to errors. |
| Result *result = MakeNode(Result); |
| |
| Plan *plan = &(result->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // keep the same costs & rows estimates |
| plan->startup_cost = child_result->startup_cost; |
| plan->total_cost = child_result->total_cost; |
| plan->plan_rows = child_result->plan_rows; |
| plan->plan_width = child_result->plan_width; |
| |
| // populate the targetlist based on child_result's original targetlist |
| plan->targetlist = NIL; |
| ListCell *lc = nullptr; |
| ULONG ul = 0; |
| ForEach(lc, child_result->targetlist) |
| { |
| if (ul++ >= project_list_dxlnode->Arity()) |
| { |
| // done with the original targetlist, stop |
| // all expressions added after project_list_dxlnode->Arity() are |
| // not output cols, but rather hash expressions and should not be projected |
| break; |
| } |
| |
| TargetEntry *te = (TargetEntry *) lfirst(lc); |
| Var *var = gpdb::MakeVar( |
| OUTER_VAR, te->resno, gpdb::ExprType((Node *) te->expr), |
| gpdb::ExprTypeMod((Node *) te->expr), 0 /* varlevelsup */); |
| TargetEntry *new_te = |
| gpdb::MakeTargetEntry((Expr *) var, ul, /* resno */ |
| te->resname, te->resjunk); |
| plan->targetlist = gpdb::LAppend(plan->targetlist, new_te); |
| } |
| |
| plan->qual = NIL; |
| plan->lefttree = child_result; |
| |
| SetParamIds(plan); |
| |
| return (Plan *) result; |
| } |
| |
| return (Plan *) result; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateAggFillInfo |
| // |
| // @doc: |
| // Fill the aggregate node with aggno and aggtransno |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslateAggFillInfo(CContextDXLToPlStmt *ctx, |
| Aggref *aggref) |
| { |
| Oid aggtransfn; |
| Oid aggfinalfn; |
| Oid aggcombinefn; |
| Oid aggserialfn; |
| Oid aggdeserialfn; |
| Oid aggtranstype; |
| int32 aggtranstypmod; |
| int32 aggtransspace; |
| |
| Datum initValue; |
| bool initValueIsNull; |
| List *same_input_transnos; |
| |
| bool shareable; |
| int16 resulttypeLen; |
| bool resulttypeByVal; |
| int16 transtypeLen; |
| bool transtypeByVal; |
| |
| int aggno, transno; |
| |
| gpdb::GetAggregateInfo(aggref, &aggtransfn, &aggfinalfn, |
| &aggcombinefn, &aggserialfn, &aggdeserialfn, |
| &aggtranstype, &aggtransspace, &initValue, |
| &initValueIsNull, &shareable); |
| |
| /* |
| * If transition state is of same type as first aggregated input, assume |
| * it's the same typmod (same width) as well. This works for cases like |
| * MAX/MIN and is probably somewhat reasonable otherwise. |
| */ |
| aggtranstypmod = -1; |
| if (aggref->args) |
| { |
| TargetEntry *tle = (TargetEntry *) linitial(aggref->args); |
| |
| if (aggtranstype == gpdb::ExprType((Node *) tle->expr)) |
| aggtranstypmod = gpdb::ExprTypeMod((Node *) tle->expr); |
| } |
| |
| gpdb::TypLenByVal(aggref->aggtype, &resulttypeLen, &resulttypeByVal); |
| |
| /* |
| * 1. See if this is identical to another aggregate function call that |
| * we've seen already. |
| */ |
| aggno = gpdb::FindCompatibleAgg(ctx->GetAggInfos(), aggref, |
| &same_input_transnos); |
| if (aggno != -1) |
| { |
| AggInfo *agginfo = (AggInfo *) gpdb::ListNth(ctx->GetAggInfos(), aggno); |
| |
| transno = agginfo->transno; |
| } |
| else |
| { |
| AggInfo *agginfo = (AggInfo *) gpdb::GPDBAlloc(sizeof(AggInfo)); |
| |
| agginfo->finalfn_oid = aggfinalfn; |
| agginfo->representative_aggref = aggref; |
| agginfo->shareable = shareable; |
| |
| aggno = gpdb::ListLength(ctx->GetAggInfos()); |
| ctx->AppendAggInfos(agginfo); |
| |
| gpdb::TypLenByVal(aggtranstype, &transtypeLen, &transtypeByVal); |
| |
| /* |
| * 2. See if this aggregate can share transition state with another |
| * aggregate that we've initialized already. |
| */ |
| transno = gpdb::FindCompatibleTrans( |
| ctx->GetAggTransInfos(), shareable, aggtransfn, aggtranstype, |
| transtypeLen, transtypeByVal, aggcombinefn, aggserialfn, |
| aggdeserialfn, initValue, initValueIsNull, same_input_transnos); |
| if (transno == -1) |
| { |
| AggTransInfo *transinfo = |
| (AggTransInfo *) gpdb::GPDBAlloc(sizeof(AggTransInfo)); |
| |
| transinfo->args = aggref->args; |
| transinfo->aggfilter = aggref->aggfilter; |
| transinfo->transfn_oid = aggtransfn; |
| transinfo->combinefn_oid = aggcombinefn; |
| transinfo->serialfn_oid = aggserialfn; |
| transinfo->deserialfn_oid = aggdeserialfn; |
| transinfo->aggtranstype = aggtranstype; |
| transinfo->aggtranstypmod = aggtranstypmod; |
| transinfo->transtypeLen = transtypeLen; |
| transinfo->transtypeByVal = transtypeByVal; |
| transinfo->aggtransspace = aggtransspace; |
| transinfo->initValue = initValue; |
| transinfo->initValueIsNull = initValueIsNull; |
| |
| transno = gpdb::ListLength(ctx->GetAggTransInfos()); |
| ctx->AppendAggTransInfos(transinfo); |
| } |
| agginfo->transno = transno; |
| } |
| |
| // setting the aggno and transno |
| aggref->aggno = aggno; |
| aggref->aggtransno = transno; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLAgg |
| // |
| // @doc: |
| // Translate DXL aggregate node into GPDB Agg plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLAgg( |
| const CDXLNode *agg_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create aggregate plan node |
| Agg *agg = MakeNode(Agg); |
| |
| Plan *plan = &(agg->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalAgg *dxl_phy_agg_dxlop = |
| CDXLPhysicalAgg::Cast(agg_dxlnode->GetOperator()); |
| |
| // translate operator costs |
| TranslatePlanCosts(agg_dxlnode, plan); |
| |
| // translate agg child |
| CDXLNode *child_dxlnode = (*agg_dxlnode)[EdxlaggIndexChild]; |
| |
| CDXLNode *project_list_dxlnode = (*agg_dxlnode)[EdxlaggIndexProjList]; |
| CDXLNode *filter_dxlnode = (*agg_dxlnode)[EdxlaggIndexFilter]; |
| |
| CDXLTranslateContext child_context(m_mp, true, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, // pdxltrctxRight, |
| &plan->targetlist, &plan->qual, output_context); |
| |
| plan->lefttree = child_plan; |
| |
| // translate aggregation strategy |
| switch (dxl_phy_agg_dxlop->GetAggStrategy()) |
| { |
| case EdxlaggstrategyPlain: |
| agg->aggstrategy = AGG_PLAIN; |
| break; |
| case EdxlaggstrategySorted: |
| agg->aggstrategy = AGG_SORTED; |
| break; |
| case EdxlaggstrategyHashed: |
| agg->aggstrategy = AGG_HASHED; |
| break; |
| default: |
| GPOS_ASSERT(!"Invalid aggregation strategy"); |
| } |
| |
| if (agg->aggstrategy == AGG_HASHED && |
| CTranslatorUtils::HasOrderedAggRefInProjList(project_list_dxlnode)) |
| { |
| GPOS_RAISE(gpopt::ExmaDXL, gpopt::ExmiExpr2DXLUnsupportedFeature, |
| GPOS_WSZ_LIT("Hash aggregation with ORDER BY")); |
| } |
| |
| agg->streaming = dxl_phy_agg_dxlop->IsStreamSafe(); |
| |
| // translate grouping cols |
| const ULongPtrArray *grouping_colid_array = |
| dxl_phy_agg_dxlop->GetGroupingColidArray(); |
| agg->numCols = grouping_colid_array->Size(); |
| if (agg->numCols > 0) |
| { |
| agg->grpColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(agg->numCols * sizeof(AttrNumber)); |
| agg->grpOperators = (Oid *) gpdb::GPDBAlloc(agg->numCols * sizeof(Oid)); |
| agg->grpCollations = |
| (Oid *) gpdb::GPDBAlloc(agg->numCols * sizeof(Oid)); |
| } |
| else |
| { |
| agg->grpColIdx = nullptr; |
| agg->grpOperators = nullptr; |
| agg->grpCollations = nullptr; |
| } |
| |
| const ULONG length = grouping_colid_array->Size(); |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| ULONG grouping_colid = *((*grouping_colid_array)[ul]); |
| const TargetEntry *target_entry_grouping_col = |
| child_context.GetTargetEntry(grouping_colid); |
| if (nullptr == target_entry_grouping_col) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| grouping_colid); |
| } |
| agg->grpColIdx[ul] = target_entry_grouping_col->resno; |
| |
| // Also find the equality operators to use for each grouping col. |
| Oid typeId = gpdb::ExprType((Node *) target_entry_grouping_col->expr); |
| agg->grpOperators[ul] = gpdb::GetEqualityOp(typeId); |
| agg->grpCollations[ul] = |
| gpdb::ExprCollation((Node *) target_entry_grouping_col->expr); |
| Assert(agg->grpOperators[ul] != 0); |
| } |
| |
| agg->numGroups = |
| std::max(1L, (long) std::min(agg->plan.plan_rows, (double) LONG_MAX)); |
| |
| // Set the aggsplit,aggno,aggtransno for the agg node |
| ListCell *lc; |
| INT aggsplit = 0; |
| ForEach (lc, plan->targetlist) |
| { |
| TargetEntry *te = (TargetEntry *) lfirst(lc); |
| if (IsA(te->expr, Aggref)) |
| { |
| Aggref *aggref = (Aggref *) te->expr; |
| |
| if (AGGSPLIT_INTERMEDIATE != aggsplit) |
| { |
| aggsplit |= aggref->aggsplit; |
| } |
| TranslateAggFillInfo(m_dxl_to_plstmt_context, aggref); |
| } |
| } |
| agg->aggsplit = (AggSplit) aggsplit; |
| |
| ForEach (lc, plan->qual) |
| { |
| Expr *expr = (Expr *) lfirst(lc); |
| if (IsA(expr, Aggref)) |
| { |
| Aggref *aggref = (Aggref *) expr; |
| // ORCA won't create the qual but a scalar in AGG |
| TranslateAggFillInfo(m_dxl_to_plstmt_context, aggref); |
| } |
| } |
| |
| m_dxl_to_plstmt_context->ResetAggInfosAndTransInfos(); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) agg; |
| } |
| |
| static |
| int WindowFrameSpecToOptions(const EdxlFrameSpec &dxlFS) { |
| int winFrameOptions = 0; |
| if (EdxlfsRow == dxlFS) |
| { |
| winFrameOptions |= FRAMEOPTION_ROWS; |
| } |
| else if (EdxlfsGroups == dxlFS) |
| { |
| winFrameOptions |= FRAMEOPTION_GROUPS; |
| } |
| else |
| { |
| winFrameOptions |= FRAMEOPTION_RANGE; |
| } |
| return winFrameOptions; |
| } |
| |
| static |
| int WindowFrameExclusionStrategyToOptions(const EdxlFrameExclusionStrategy &dxlFES) { |
| int winFrameOptions = 0; |
| if (dxlFES == EdxlfesCurrentRow) |
| { |
| winFrameOptions |= FRAMEOPTION_EXCLUDE_CURRENT_ROW; |
| } |
| else if (dxlFES == EdxlfesGroup) |
| { |
| winFrameOptions |= FRAMEOPTION_EXCLUDE_GROUP; |
| } |
| else if (dxlFES == EdxlfesTies) |
| { |
| winFrameOptions |= FRAMEOPTION_EXCLUDE_TIES; |
| } |
| |
| return winFrameOptions; |
| } |
| |
| static |
| int WindowFrameStartBoundaryToOptions(const EdxlFrameBoundary &dxlFB) { |
| int winFrameOptions = 0; |
| if (dxlFB == EdxlfbUnboundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_START_UNBOUNDED_PRECEDING; |
| } |
| if (dxlFB == EdxlfbBoundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_START_OFFSET_PRECEDING; |
| } |
| if (dxlFB == EdxlfbCurrentRow) |
| { |
| winFrameOptions |= FRAMEOPTION_START_CURRENT_ROW; |
| } |
| if (dxlFB == EdxlfbBoundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_START_OFFSET_FOLLOWING; |
| } |
| if (dxlFB == EdxlfbUnboundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_START_UNBOUNDED_FOLLOWING; |
| } |
| if (dxlFB == EdxlfbDelayedBoundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_START_OFFSET_PRECEDING; |
| } |
| if (dxlFB == EdxlfbDelayedBoundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_START_OFFSET_FOLLOWING; |
| } |
| return winFrameOptions; |
| } |
| |
| static |
| int WindowFrameEndBoundaryToOptions(const EdxlFrameBoundary &dxlFB) { |
| int winFrameOptions = 0; |
| if (dxlFB == EdxlfbUnboundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_END_UNBOUNDED_PRECEDING; |
| } |
| if (dxlFB == EdxlfbBoundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_END_OFFSET_PRECEDING; |
| } |
| if (dxlFB == EdxlfbCurrentRow) |
| { |
| winFrameOptions |= FRAMEOPTION_END_CURRENT_ROW; |
| } |
| if (dxlFB == EdxlfbBoundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_END_OFFSET_FOLLOWING; |
| } |
| if (dxlFB == EdxlfbUnboundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_END_UNBOUNDED_FOLLOWING; |
| } |
| if (dxlFB == EdxlfbDelayedBoundedPreceding) |
| { |
| winFrameOptions |= FRAMEOPTION_END_OFFSET_PRECEDING; |
| } |
| if (dxlFB == EdxlfbDelayedBoundedFollowing) |
| { |
| winFrameOptions |= FRAMEOPTION_END_OFFSET_FOLLOWING; |
| } |
| return winFrameOptions; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLWindowAgg |
| // |
| // @doc: |
| // Translate DXL window node into GPDB window plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLWindowAgg( |
| const CDXLNode *window_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create a WindowAgg plan node |
| WindowAgg *window = MakeNode(WindowAgg); |
| |
| Plan *plan = &(window->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalWindow *window_dxlop = |
| CDXLPhysicalWindow::Cast(window_dxlnode->GetOperator()); |
| |
| // translate the operator costs |
| TranslatePlanCosts(window_dxlnode, plan); |
| |
| // translate children |
| CDXLNode *child_dxlnode = (*window_dxlnode)[EdxlwindowIndexChild]; |
| CDXLNode *project_list_dxlnode = (*window_dxlnode)[EdxlwindowIndexProjList]; |
| CDXLNode *filter_dxlnode = (*window_dxlnode)[EdxlwindowIndexFilter]; |
| |
| CDXLTranslateContext child_context(m_mp, true, |
| output_context->GetColIdToParamIdMap()); |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, // pdxltrctxRight, |
| &plan->targetlist, &plan->qual, output_context); |
| |
| ListCell *lc; |
| |
| foreach (lc, plan->targetlist) |
| { |
| TargetEntry *target_entry = (TargetEntry *) lfirst(lc); |
| if (IsA(target_entry->expr, WindowFunc)) |
| { |
| WindowFunc *window_func = (WindowFunc *) target_entry->expr; |
| window->winref = window_func->winref; |
| break; |
| } |
| } |
| |
| plan->lefttree = child_plan; |
| |
| // translate partition columns |
| const ULongPtrArray *part_by_cols_array = |
| window_dxlop->GetPartByColsArray(); |
| window->partNumCols = part_by_cols_array->Size(); |
| |
| if (window->partNumCols > 0) |
| { |
| window->partColIdx = (AttrNumber *) gpdb::GPDBAlloc( |
| window->partNumCols * sizeof(AttrNumber)); |
| window->partOperators = |
| (Oid *) gpdb::GPDBAlloc(window->partNumCols * sizeof(Oid)); |
| window->partCollations = |
| (Oid *) gpdb::GPDBAlloc(window->partNumCols * sizeof(Oid)); |
| } else { |
| window->partColIdx = nullptr; |
| window->partOperators = nullptr; |
| window->partCollations = nullptr; |
| } |
| |
| const ULONG num_of_part_cols = part_by_cols_array->Size(); |
| for (ULONG ul = 0; ul < num_of_part_cols; ul++) |
| { |
| ULONG part_colid = *((*part_by_cols_array)[ul]); |
| const TargetEntry *te_part_colid = |
| child_context.GetTargetEntry(part_colid); |
| if (nullptr == te_part_colid) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| part_colid); |
| } |
| window->partColIdx[ul] = te_part_colid->resno; |
| |
| // Also find the equality operators to use for each partitioning key col. |
| Oid type_id = gpdb::ExprType((Node *) te_part_colid->expr); |
| window->partOperators[ul] = gpdb::GetEqualityOp(type_id); |
| Assert(window->partOperators[ul] != 0); |
| window->partCollations[ul] = |
| gpdb::ExprCollation((Node *) te_part_colid->expr); |
| } |
| |
| // translate window keys |
| const ULONG size = window_dxlop->WindowKeysCount(); |
| if (size > 1) |
| { |
| GpdbEreport(ERRCODE_INTERNAL_ERROR, ERROR, |
| "ORCA produced a plan with more than one window key", |
| nullptr); |
| } |
| GPOS_ASSERT(size <= 1 && "cannot have more than one window key"); |
| |
| if (size == 1) |
| { |
| // translate the sorting columns used in the window key |
| const CDXLWindowKey *window_key = window_dxlop->GetDXLWindowKeyAt(0); |
| const CDXLWindowFrame *window_frame = window_key->GetWindowFrame(); |
| const CDXLNode *sort_col_list_dxlnode = window_key->GetSortColListDXL(); |
| |
| const ULONG num_of_cols = sort_col_list_dxlnode->Arity(); |
| |
| window->ordNumCols = num_of_cols; |
| window->ordColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(num_of_cols * sizeof(AttrNumber)); |
| window->ordOperators = |
| (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| window->ordCollations = |
| (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| bool *is_nulls_first = |
| (bool *) gpdb::GPDBAlloc(num_of_cols * sizeof(bool)); |
| TranslateSortCols(sort_col_list_dxlnode, &child_context, |
| window->ordColIdx, window->ordOperators, |
| window->ordCollations, is_nulls_first); |
| |
| // The firstOrder* fields are separate from just picking the first of ordCol*, |
| // because the Postgres planner might omit columns that are redundant with the |
| // PARTITION BY from ordCol*. But ORCA doesn't do that, so we can just copy |
| // the first entry of ordColIdx/ordOperators into firstOrder* fields. |
| if (num_of_cols > 0) |
| { |
| window->firstOrderCol = window->ordColIdx[0]; |
| window->firstOrderCmpOperator = window->ordOperators[0]; |
| window->firstOrderNullsFirst = is_nulls_first[0]; |
| } |
| gpdb::GPDBFree(is_nulls_first); |
| |
| // The ordOperators array is actually supposed to contain equality operators, |
| // not ordering operators (< or >). So look up the corresponding equality |
| // operator for each ordering operator. |
| for (ULONG i = 0; i < num_of_cols; i++) |
| { |
| window->ordOperators[i] = gpdb::GetEqualityOpForOrderingOp( |
| window->ordOperators[i], nullptr); |
| } |
| |
| // translate the window frame specified in the window key |
| if (nullptr != window_key->GetWindowFrame()) |
| { |
| window->frameOptions = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_BETWEEN; |
| window->frameOptions |= WindowFrameSpecToOptions(window_frame->ParseDXLFrameSpec()); |
| window->frameOptions |= WindowFrameExclusionStrategyToOptions( |
| window_frame->ParseFrameExclusionStrategy()); |
| |
| // translate the CDXLNodes representing the leading and trailing edge |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| // Translate lead boundary |
| // |
| // Note that we don't distinguish between the delayed and undelayed |
| // versions beoynd this point. Executor will make that decision |
| // without our help. |
| // |
| CDXLNode *win_frame_leading_dxlnode = window_frame->PdxlnLeading(); |
| window->frameOptions |= WindowFrameStartBoundaryToOptions( |
| CDXLScalarWindowFrameEdge::Cast(win_frame_leading_dxlnode->GetOperator()) |
| ->ParseDXLFrameBoundary()); |
| if (0 != win_frame_leading_dxlnode->Arity()) |
| { |
| window->startOffset = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*win_frame_leading_dxlnode)[0], &colid_var_mapping); |
| } |
| |
| // And the same for the trail boundary |
| CDXLNode *win_frame_trailing_dxlnode = |
| window_frame->PdxlnTrailing(); |
| window->frameOptions |= WindowFrameEndBoundaryToOptions( |
| CDXLScalarWindowFrameEdge::Cast( |
| win_frame_trailing_dxlnode->GetOperator()) |
| ->ParseDXLFrameBoundary()); |
| |
| if (0 != win_frame_trailing_dxlnode->Arity()) |
| { |
| window->endOffset = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*win_frame_trailing_dxlnode)[0], &colid_var_mapping); |
| } |
| |
| window->startInRangeFunc = window_frame->PdxlnStartInRangeFunc(); |
| window->endInRangeFunc = window_frame->PdxlnEndInRangeFunc(); |
| window->inRangeColl = window_frame->PdxlnInRangeColl(); |
| window->inRangeAsc = window_frame->PdxlnInRangeAsc(); |
| window->inRangeNullsFirst = window_frame->PdxlnInRangeNullsFirst(); |
| |
| // cleanup |
| child_contexts->Release(); |
| } |
| else |
| { |
| window->frameOptions = FRAMEOPTION_DEFAULTS; |
| } |
| } |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) window; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLWindowHashAgg |
| // |
| // @doc: |
| // Translate DXL window node into GPDB window hash plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLWindowHashAgg( |
| const CDXLNode *window_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| WindowHashAgg *window = MakeNode(WindowHashAgg); |
| |
| Plan *plan = &(window->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalWindow *window_dxlop = |
| CDXLPhysicalWindow::Cast(window_dxlnode->GetOperator()); |
| |
| // translate the operator costs |
| TranslatePlanCosts(window_dxlnode, plan); |
| |
| // translate children |
| CDXLNode *child_dxlnode = (*window_dxlnode)[EdxlwindowIndexChild]; |
| CDXLNode *project_list_dxlnode = (*window_dxlnode)[EdxlwindowIndexProjList]; |
| CDXLNode *filter_dxlnode = (*window_dxlnode)[EdxlwindowIndexFilter]; |
| |
| CDXLTranslateContext child_context(m_mp, true, |
| output_context->GetColIdToParamIdMap()); |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, // pdxltrctxRight, |
| &plan->targetlist, &plan->qual, output_context); |
| |
| ListCell *lc; |
| |
| foreach (lc, plan->targetlist) |
| { |
| TargetEntry *target_entry = (TargetEntry *) lfirst(lc); |
| if (IsA(target_entry->expr, WindowFunc)) |
| { |
| WindowFunc *window_func = (WindowFunc *) target_entry->expr; |
| window->winref = window_func->winref; |
| break; |
| } |
| } |
| |
| plan->lefttree = child_plan; |
| |
| // translate partition columns |
| const ULongPtrArray *part_by_cols_array = |
| window_dxlop->GetPartByColsArray(); |
| window->partNumCols = part_by_cols_array->Size(); |
| |
| if (window->partNumCols > 0) |
| { |
| window->partColIdx = (AttrNumber *) gpdb::GPDBAlloc( |
| window->partNumCols * sizeof(AttrNumber)); |
| window->partOperators = |
| (Oid *) gpdb::GPDBAlloc(window->partNumCols * sizeof(Oid)); |
| window->partCollations = |
| (Oid *) gpdb::GPDBAlloc(window->partNumCols * sizeof(Oid)); |
| } else { |
| window->partColIdx = nullptr; |
| window->partOperators = nullptr; |
| window->partCollations = nullptr; |
| } |
| |
| const ULONG num_of_part_cols = part_by_cols_array->Size(); |
| for (ULONG ul = 0; ul < num_of_part_cols; ul++) |
| { |
| ULONG part_colid = *((*part_by_cols_array)[ul]); |
| const TargetEntry *te_part_colid = |
| child_context.GetTargetEntry(part_colid); |
| if (nullptr == te_part_colid) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| part_colid); |
| } |
| window->partColIdx[ul] = te_part_colid->resno; |
| |
| // Also find the equality operators to use for each partitioning key col. |
| Oid type_id = gpdb::ExprType((Node *) te_part_colid->expr); |
| window->partOperators[ul] = gpdb::GetEqualityOp(type_id); |
| Assert(window->partOperators[ul] != 0); |
| window->partCollations[ul] = |
| gpdb::ExprCollation((Node *) te_part_colid->expr); |
| } |
| |
| // translate window keys |
| const ULONG size = window_dxlop->WindowKeysCount(); |
| if (size > 1) |
| { |
| GpdbEreport(ERRCODE_INTERNAL_ERROR, ERROR, |
| "ORCA produced a plan with more than one window key", |
| nullptr); |
| } |
| GPOS_ASSERT(size <= 1 && "cannot have more than one window key"); |
| |
| if (size == 1) |
| { |
| // translate the sorting columns used in the window key |
| const CDXLWindowKey *window_key = window_dxlop->GetDXLWindowKeyAt(0); |
| const CDXLWindowFrame *window_frame = window_key->GetWindowFrame(); |
| const CDXLNode *sort_col_list_dxlnode = window_key->GetSortColListDXL(); |
| |
| const ULONG num_of_cols = sort_col_list_dxlnode->Arity(); |
| |
| window->ordNumCols = num_of_cols; |
| window->ordColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(num_of_cols * sizeof(AttrNumber)); |
| window->ordOperators = |
| (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| window->ordCollations = |
| (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| window->ordNullsFirst = |
| (bool *) gpdb::GPDBAlloc(num_of_cols * sizeof(bool)); |
| // different with windowagg, sort should be full |
| TranslateSortCols(sort_col_list_dxlnode, &child_context, |
| window->ordColIdx, window->ordOperators, |
| window->ordCollations, window->ordNullsFirst); |
| |
| // translate the window frame specified in the window key |
| if (nullptr != window_key->GetWindowFrame()) |
| { |
| window->frameOptions = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_BETWEEN; |
| window->frameOptions |= WindowFrameSpecToOptions(window_frame->ParseDXLFrameSpec()); |
| window->frameOptions |= WindowFrameExclusionStrategyToOptions( |
| window_frame->ParseFrameExclusionStrategy()); |
| |
| // translate the CDXLNodes representing the leading and trailing edge |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| // Translate lead boundary |
| // |
| // Note that we don't distinguish between the delayed and undelayed |
| // versions beoynd this point. Executor will make that decision |
| // without our help. |
| // |
| CDXLNode *win_frame_leading_dxlnode = window_frame->PdxlnLeading(); |
| window->frameOptions |= WindowFrameStartBoundaryToOptions( |
| CDXLScalarWindowFrameEdge::Cast(win_frame_leading_dxlnode->GetOperator()) |
| ->ParseDXLFrameBoundary()); |
| if (0 != win_frame_leading_dxlnode->Arity()) |
| { |
| window->startOffset = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*win_frame_leading_dxlnode)[0], &colid_var_mapping); |
| } |
| |
| // And the same for the trail boundary |
| CDXLNode *win_frame_trailing_dxlnode = |
| window_frame->PdxlnTrailing(); |
| window->frameOptions |= WindowFrameEndBoundaryToOptions( |
| CDXLScalarWindowFrameEdge::Cast( |
| win_frame_trailing_dxlnode->GetOperator()) |
| ->ParseDXLFrameBoundary()); |
| |
| if (0 != win_frame_trailing_dxlnode->Arity()) |
| { |
| window->endOffset = |
| (Node *) m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*win_frame_trailing_dxlnode)[0], &colid_var_mapping); |
| } |
| |
| window->startInRangeFunc = window_frame->PdxlnStartInRangeFunc(); |
| window->endInRangeFunc = window_frame->PdxlnEndInRangeFunc(); |
| window->inRangeColl = window_frame->PdxlnInRangeColl(); |
| window->inRangeAsc = window_frame->PdxlnInRangeAsc(); |
| window->inRangeNullsFirst = window_frame->PdxlnInRangeNullsFirst(); |
| |
| // cleanup |
| child_contexts->Release(); |
| } |
| else |
| { |
| window->frameOptions = FRAMEOPTION_DEFAULTS; |
| } |
| } |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) window; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLSort |
| // |
| // @doc: |
| // Translate DXL sort node into GPDB Sort plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLSort( |
| const CDXLNode *sort_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // Ensure operator of sort_dxlnode exists and is EdxlopPhysicalSort |
| GPOS_ASSERT(nullptr != sort_dxlnode->GetOperator()); |
| GPOS_ASSERT(EdxlopPhysicalSort == |
| sort_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| // create sort plan node |
| Sort *sort = MakeNode(Sort); |
| |
| Plan *plan = &(sort->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(sort_dxlnode, plan); |
| |
| // translate sort child |
| CDXLNode *child_dxlnode = (*sort_dxlnode)[EdxlsortIndexChild]; |
| CDXLNode *project_list_dxlnode = (*sort_dxlnode)[EdxlsortIndexProjList]; |
| CDXLNode *filter_dxlnode = (*sort_dxlnode)[EdxlsortIndexFilter]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| plan->lefttree = child_plan; |
| |
| // translate sorting columns |
| |
| const CDXLNode *sort_col_list_dxl = |
| (*sort_dxlnode)[EdxlsortIndexSortColList]; |
| |
| const ULONG num_of_cols = sort_col_list_dxl->Arity(); |
| sort->numCols = num_of_cols; |
| sort->sortColIdx = |
| (AttrNumber *) gpdb::GPDBAlloc(num_of_cols * sizeof(AttrNumber)); |
| sort->sortOperators = (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| sort->collations = (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid)); |
| sort->nullsFirst = (bool *) gpdb::GPDBAlloc(num_of_cols * sizeof(bool)); |
| |
| TranslateSortCols(sort_col_list_dxl, &child_context, sort->sortColIdx, |
| sort->sortOperators, sort->collations, sort->nullsFirst); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) sort; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // If the top level is not a function returning set then we need to check if the |
| // project element contains any SRF's deep down the tree. If we found any SRF's |
| // at lower levels then we will require a result node on top of ProjectSet node. |
| // Eg. |
| // <dxl:ProjElem ColId="1" Alias="abs"> |
| // <dxl:FuncExpr FuncId="0.1397.1.0" FuncRetSet="false" TypeMdid="0.23.1.0"> |
| // <dxl:FuncExpr FuncId="0.1067.1.0" FuncRetSet="true" TypeMdid="0.23.1.0"> |
| // ... |
| // </dxl:FuncExpr> |
| // </dxl:FuncExpr> |
| // Here we have SRF present at a lower level. So we will require a result node |
| // on top. |
| //------------------------------------------------------------------------------ |
| static BOOL |
| ContainsLowLevelSetReturningFunc(const CDXLNode *scalar_expr_dxlnode) |
| { |
| const ULONG arity = scalar_expr_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *expr_dxlnode = (*scalar_expr_dxlnode)[ul]; |
| CDXLOperator *op = expr_dxlnode->GetOperator(); |
| Edxlopid dxlopid = op->GetDXLOperator(); |
| |
| if ((EdxlopScalarFuncExpr == dxlopid && |
| CDXLScalarFuncExpr::Cast(op)->ReturnsSet()) || |
| ContainsLowLevelSetReturningFunc(expr_dxlnode)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // This method is required to check if we need a result node on top of |
| // ProjectSet node. If the project element contains SRF on top then we don't |
| // require a result node. Eg |
| // <dxl:ProjElem ColId="1" Alias="generate_series"> |
| // <dxl:FuncExpr FuncId="0.1067.1.0" FuncRetSet="true" TypeMdid="0.23.1.0"> |
| // ... |
| // <dxl:FuncExpr FuncId="0.1067.1.0" FuncRetSet="true" TypeMdid="0.23.1.0"> |
| // ... |
| // </dxl:FuncExpr> |
| // ... |
| // </dxl:FuncExpr> |
| // Here we have a FuncExpr which returns a set on top. So we don't require a |
| // result node on top of ProjectSet node. |
| //------------------------------------------------------------------------------ |
| static BOOL |
| RequiresResultNode(const CDXLNode *project_list_dxlnode) |
| { |
| const ULONG arity = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ++ul) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| GPOS_ASSERT(EdxlopScalarProjectElem == |
| proj_elem_dxlnode->GetOperator()->GetDXLOperator()); |
| GPOS_ASSERT(1 == proj_elem_dxlnode->Arity()); |
| CDXLNode *expr_dxlnode = (*proj_elem_dxlnode)[0]; |
| CDXLOperator *op = expr_dxlnode->GetOperator(); |
| Edxlopid dxlopid = op->GetDXLOperator(); |
| if (EdxlopScalarFuncExpr == dxlopid) |
| { |
| if (!(CDXLScalarFuncExpr::Cast(op)->ReturnsSet()) && |
| ContainsLowLevelSetReturningFunc(expr_dxlnode)) |
| { |
| return true; |
| } |
| } |
| else |
| { |
| if (ContainsLowLevelSetReturningFunc(expr_dxlnode)) |
| { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLProjectSet |
| // |
| // @doc: |
| // Translate DXL result node into project set node if SRF's are present |
| // |
| //------------------------------------------------------------------------------ |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLProjectSet(const CDXLNode *result_dxlnode) |
| { |
| // ORCA_FEATURE_NOT_SUPPORTED: The Project Set nodes don't support a qual in |
| // the planned statement. Just being defensive here for the case when the |
| // result dxl node has a set returning function in the project list and also |
| // a qual. In that case will not create a ProjectSet node and will fall back |
| // to planner. |
| if ((*result_dxlnode)[EdxlresultIndexFilter]->Arity() > 0) |
| { |
| GPOS_RAISE( |
| gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, |
| GPOS_WSZ_LIT("Unsupported one-time filter in ProjectSet node")); |
| } |
| |
| // create project set plan node |
| ProjectSet *project_set = MakeNode(ProjectSet); |
| |
| Plan *plan = &(project_set->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(result_dxlnode, plan); |
| |
| SetParamIds(plan); |
| |
| return (Plan *) project_set; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // @function: |
| // CTranslatorDXLToPlStmt::CreateProjectSetNodeTree |
| // |
| // @doc: |
| // Creates a tree of project set plan nodes to contain the SRF's |
| // |
| //------------------------------------------------------------------------------ |
| Plan * |
| CTranslatorDXLToPlStmt::CreateProjectSetNodeTree(const CDXLNode *result_dxlnode, |
| Plan *result_node_plan, |
| Plan *child_plan, |
| Plan *&project_set_child_plan, |
| BOOL &will_require_result_node) |
| { |
| // Method split_pathtarget_at_srfs will split the given PathTarget into |
| // multiple levels to position SRFs safely. This list will hold the splited |
| // PathTarget created by split_pathtarget_at_srfs method. |
| List *targets_with_srf = NIL; |
| |
| // List of bool flags indicating whether the corresponding PathTarget |
| // contains any evaluatable SRFs |
| List *targets_with_srf_bool = NIL; |
| |
| // Pointer to the top level ProjectSet node. If a result node is required |
| // then this will be attached to the lefttree of the result node. |
| Plan *project_set_parent_plan = nullptr; |
| |
| // Create Pathtarget object from Result node's targetlist which is required |
| // by SplitPathtargetAtSrfs method |
| PathTarget *complete_result_pathtarget = |
| gpdb::MakePathtargetFromTlist(result_node_plan->targetlist); |
| |
| // Split given PathTarget into multiple levels to position SRFs safely |
| gpdb::SplitPathtargetAtSrfs(nullptr, complete_result_pathtarget, nullptr, |
| &targets_with_srf, &targets_with_srf_bool); |
| |
| // If the PathTarget created from Result node's targetlist does not contain |
| // any set returning functions then split_pathtarget_at_srfs method will |
| // return the same PathTarget back. In this case a ProjectSet node is not |
| // required. |
| if (1 == gpdb::ListLength(targets_with_srf)) |
| { |
| return nullptr; |
| } |
| |
| // Do we require a result node to be attached on top of ProjectSet node? |
| will_require_result_node = |
| RequiresResultNode((*result_dxlnode)[EdxlresultIndexProjList]); |
| |
| ListCell *lc; |
| ULONG list_cell_pos = 1; |
| ULONG targets_with_srf_list_length = gpdb::ListLength(targets_with_srf); |
| |
| ForEach(lc, targets_with_srf) |
| { |
| // The first element of the PathTarget list created by |
| // split_pathtarget_at_srfs method will not contain any |
| // SRF's. So skipping it. |
| if (list_cell_pos == 1) |
| { |
| list_cell_pos++; |
| continue; |
| } |
| |
| // If a Result node is required on top of a ProjectSet node then the |
| // last element of PathTarget list created by split_pathtarget_at_srfs |
| // method will contain the PathTarget of the result node. Since result |
| // node is already created before, breaking out from the loop. If a |
| // result node is not required on top of a ProjectSet node, continue to |
| // create a ProjectSet node. |
| if (will_require_result_node && |
| targets_with_srf_list_length == list_cell_pos) |
| { |
| break; |
| } |
| |
| list_cell_pos++; |
| |
| List *target_list_entry = |
| gpdb::MakeTlistFromPathtarget((PathTarget *) lfirst(lc)); |
| |
| Plan *temp_plan_project_set = TranslateDXLProjectSet(result_dxlnode); |
| |
| temp_plan_project_set->targetlist = target_list_entry; |
| |
| // Creating the links between all the nested ProjectSet nodes |
| if (nullptr == project_set_parent_plan) |
| { |
| project_set_parent_plan = temp_plan_project_set; |
| project_set_child_plan = temp_plan_project_set; |
| } |
| else |
| { |
| temp_plan_project_set->lefttree = project_set_parent_plan; |
| project_set_parent_plan = temp_plan_project_set; |
| } |
| } |
| |
| return project_set_parent_plan; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // If a result plan node is not required on top of a project set node then the |
| // alias parameter needs to be set for all the project set nodes else not |
| // required as that information will already be present in the result node |
| // created |
| //------------------------------------------------------------------------------ |
| void |
| SetupAliasParameter(const BOOL will_require_result_node, |
| const CDXLNode *project_list_dxlnode, |
| Plan *project_set_parent_plan) |
| { |
| if (!will_require_result_node) |
| { |
| // Setting up the alias value (te->resname) |
| ULONG ul = 0; |
| ListCell *listcell_project_targetentry; |
| |
| ForEach(listcell_project_targetentry, |
| project_set_parent_plan->targetlist) |
| { |
| TargetEntry *te = |
| (TargetEntry *) lfirst(listcell_project_targetentry); |
| |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| |
| GPOS_ASSERT(EdxlopScalarProjectElem == |
| proj_elem_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| CDXLScalarProjElem *sc_proj_elem_dxlop = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| |
| GPOS_ASSERT(1 == proj_elem_dxlnode->Arity()); |
| |
| te->resname = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| sc_proj_elem_dxlop->GetMdNameAlias() |
| ->GetMDName() |
| ->GetBuffer()); |
| ul++; |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| // This method is used to convert the FUNCEXPR present in upper level |
| // Result/ProjectSet nodes targetlist to VAR nodes which reference the FUNCEXPR |
| // present in the leftree plan targetlist. |
| //------------------------------------------------------------------------------ |
| void |
| CTranslatorDXLToPlStmt::MutateFuncExprToVarProjectSet(Plan *final_plan) |
| { |
| Plan *it_set_upper_ref = final_plan; |
| while (it_set_upper_ref->lefttree != nullptr) |
| { |
| Plan *subplan = it_set_upper_ref->lefttree; |
| List *output_targetlist; |
| ListCell *l; |
| output_targetlist = NIL; |
| |
| foreach (l, it_set_upper_ref->targetlist) |
| { |
| TargetEntry *tle = (TargetEntry *) lfirst(l); |
| Node *newexpr; |
| |
| newexpr = FixUpperExprMutatorProjectSet((Node *) tle->expr, |
| subplan->targetlist); |
| tle = gpdb::FlatCopyTargetEntry(tle); |
| tle->expr = (Expr *) newexpr; |
| output_targetlist = lappend(output_targetlist, tle); |
| } |
| it_set_upper_ref->targetlist = output_targetlist; |
| it_set_upper_ref = it_set_upper_ref->lefttree; |
| } |
| } |
| |
| Var * |
| SearchTlistForNonVarProjectset(Expr *node, List *itlist, Index newvarno) |
| { |
| TargetEntry *tle; |
| |
| if (IsA(node, Const)) |
| { |
| return nullptr; |
| } |
| |
| tle = gpdb::TlistMember(node, itlist); |
| if (nullptr != tle) |
| { |
| /* Found a matching subplan output expression */ |
| Var *newvar; |
| |
| newvar = gpdb::MakeVarFromTargetEntry(newvarno, tle); |
| newvar->varnosyn = 0; |
| newvar->varattnosyn = 0; |
| return newvar; |
| } |
| return nullptr; /* no match */ |
| } |
| |
| Node * |
| CTranslatorDXLToPlStmt::FixUpperExprMutatorProjectSet(Node *node, void *context) |
| { |
| Var *newvar; |
| |
| if (node == nullptr) |
| { |
| return nullptr; |
| } |
| |
| newvar = SearchTlistForNonVarProjectset((Expr *) node, (List *) context, OUTER_VAR); |
| if (nullptr != newvar) |
| { |
| return (Node *) newvar; |
| } |
| |
| return gpdb::Expression_tree_mutator( |
| node, |
| &CTranslatorDXLToPlStmt::FixUpperExprMutatorProjectSet, |
| context); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLResult |
| // |
| // @doc: |
| // Translate DXL result node into GPDB result plan node and create Project |
| // Set plan node if SRV are present. The current approach is to create a |
| // Project Set plan node from a result dxl node as it already contains the |
| // info to create a project set node from it. But it's not the best |
| // approach. The better approach will be to actually create a new Clogical |
| // node to handle the set returning functions and then creating CPhysical, |
| // dxl and plan nodes. |
| //------------------------------------------------------------------------------ |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLResult( |
| const CDXLNode *result_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // Pointer to the child plan of result node |
| Plan *child_plan = nullptr; |
| |
| // Pointer to the lowest level ProjectSet node. If multiple ProjectSet nodes |
| // are required then the child plan of result dxl node will be attched to |
| // its lefttree. |
| Plan *project_set_child_plan = nullptr; |
| |
| // Do we require a result node to be attached on top of ProjectSet node? |
| BOOL will_require_result_node = false; |
| |
| // create result plan node |
| Result *result = MakeNode(Result); |
| Plan *plan = &(result->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(result_dxlnode, plan); |
| |
| CDXLNode *child_dxlnode = nullptr; |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| if (result_dxlnode->Arity() - 1 == EdxlresultIndexChild) |
| { |
| // translate child plan |
| child_dxlnode = (*result_dxlnode)[EdxlresultIndexChild]; |
| child_plan = TranslateDXLOperatorToPlan(child_dxlnode, &child_context, |
| ctxt_translation_prev_siblings); |
| GPOS_ASSERT(nullptr != child_plan && "child plan cannot be NULL"); |
| } |
| |
| CDXLNode *project_list_dxlnode = (*result_dxlnode)[EdxlresultIndexProjList]; |
| CDXLNode *filter_dxlnode = (*result_dxlnode)[EdxlresultIndexFilter]; |
| CDXLNode *one_time_filter_dxlnode = |
| (*result_dxlnode)[EdxlresultIndexOneTimeFilter]; |
| List *quals_list = nullptr; |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &quals_list, |
| output_context); |
| |
| // translate one time filter |
| List *one_time_quals_list = |
| TranslateDXLFilterToQual(one_time_filter_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| plan->qual = quals_list; |
| result->resconstantqual = (Node *) one_time_quals_list; |
| SetParamIds(plan); |
| |
| // Creating project set nodes plan tree |
| Plan *project_set_parent_plan = CreateProjectSetNodeTree( |
| result_dxlnode, plan, child_plan, project_set_child_plan, |
| will_require_result_node); |
| |
| // If Project Set plan nodes are not required return the result plan node |
| // created |
| if (nullptr == project_set_parent_plan) |
| { |
| result->plan.lefttree = child_plan; |
| child_contexts->Release(); |
| return (Plan *) result; |
| } |
| |
| SetupAliasParameter(will_require_result_node, project_list_dxlnode, |
| project_set_parent_plan); |
| |
| Plan *final_plan = nullptr; |
| |
| if (will_require_result_node) |
| { |
| result->plan.lefttree = project_set_parent_plan; |
| final_plan = &(result->plan); |
| } |
| else |
| { |
| final_plan = project_set_parent_plan; |
| } |
| |
| MutateFuncExprToVarProjectSet(final_plan); |
| |
| // Attaching the child plan |
| project_set_child_plan->lefttree = child_plan; |
| |
| // cleanup |
| child_contexts->Release(); |
| return final_plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLPartSelector |
| // |
| // @doc: |
| // Translate DXL PartitionSelector into a GPDB PartitionSelector node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLPartSelector( |
| const CDXLNode *partition_selector_dxlnode, |
| CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| PartitionSelector *partition_selector = MakeNode(PartitionSelector); |
| |
| Plan *plan = &(partition_selector->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| CDXLPhysicalPartitionSelector *partition_selector_dxlop = |
| CDXLPhysicalPartitionSelector::Cast( |
| partition_selector_dxlnode->GetOperator()); |
| |
| TranslatePlanCosts(partition_selector_dxlnode, plan); |
| |
| CDXLNode *child_dxlnode = nullptr; |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| // translate child plan |
| child_dxlnode = (*partition_selector_dxlnode)[2]; |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| GPOS_ASSERT(nullptr != child_plan && "child plan cannot be NULL"); |
| |
| partition_selector->plan.lefttree = child_plan; |
| |
| child_contexts->Append(&child_context); |
| |
| CDXLNode *project_list_dxlnode = (*partition_selector_dxlnode)[0]; |
| plan->targetlist = TranslateDXLProjList(project_list_dxlnode, |
| nullptr /*base_table_context*/, |
| child_contexts, output_context); |
| |
| CMDIdGPDB *mdid = |
| CMDIdGPDB::CastMdid(partition_selector_dxlop->GetRelMdId()); |
| gpdb::RelationWrapper relation = gpdb::GetRelation(mdid->Oid()); |
| |
| CMappingColIdVarPlStmt colid_var_mapping = CMappingColIdVarPlStmt( |
| m_mp, nullptr /*base_table_context*/, child_contexts, output_context, |
| m_dxl_to_plstmt_context); |
| |
| // paramid |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| partition_selector->paramid = |
| m_dxl_to_plstmt_context->GetParamIdForSelector( |
| oid_type, partition_selector_dxlop->SelectorId()); |
| |
| // search the rtable for rtindex |
| // an Append node on the outer side of a parent HashJoin would already have |
| // beeen translated and would have populated the rtable with the root RTE |
| Index rtindex = m_dxl_to_plstmt_context->FindRTE(mdid->Oid()); |
| GPOS_ASSERT(rtindex > 0); |
| |
| // part_prune_info |
| CDXLNode *filterNode = (*partition_selector_dxlnode)[1]; |
| |
| ULongPtrArray *part_indexes = partition_selector_dxlop->Partitions(); |
| List *prune_infos = CPartPruneStepsBuilder::CreatePartPruneInfos( |
| filterNode, relation.get(), rtindex, part_indexes, &colid_var_mapping, |
| m_translator_dxl_to_scalar); |
| |
| partition_selector->part_prune_info = MakeNode(PartitionPruneInfo); |
| partition_selector->part_prune_info->prune_infos = prune_infos; |
| |
| SetParamIds(plan); |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) partition_selector; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLFilterList |
| // |
| // @doc: |
| // Translate DXL filter list into GPDB filter list |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLFilterList( |
| const CDXLNode *filter_list_dxlnode, |
| const CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *child_contexts, |
| CDXLTranslateContext *output_context) |
| { |
| GPOS_ASSERT(EdxlopScalarOpList == |
| filter_list_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| List *filters_list = NIL; |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, base_table_context, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| const ULONG arity = filter_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *child_filter_dxlnode = (*filter_list_dxlnode)[ul]; |
| |
| if (gpdxl::CTranslatorDXLToScalar::HasConstTrue(child_filter_dxlnode, |
| m_md_accessor)) |
| { |
| filters_list = gpdb::LAppend(filters_list, nullptr /*datum*/); |
| continue; |
| } |
| |
| Expr *filter_expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| child_filter_dxlnode, &colid_var_mapping); |
| filters_list = gpdb::LAppend(filters_list, filter_expr); |
| } |
| |
| return filters_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLAppend |
| // |
| // @doc: |
| // Translate DXL append node into GPDB Append plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLAppend( |
| const CDXLNode *append_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create append plan node |
| Append *append = MakeNode(Append); |
| |
| Plan *plan = &(append->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(append_dxlnode, plan); |
| |
| const ULONG arity = append_dxlnode->Arity(); |
| GPOS_ASSERT(EdxlappendIndexFirstChild < arity); |
| append->appendplans = NIL; |
| |
| // translate table descriptor into a range table entry |
| CDXLPhysicalAppend *phy_append_dxlop = |
| CDXLPhysicalAppend::Cast(append_dxlnode->GetOperator()); |
| |
| // If this append was create from a DynamicTableScan node in ORCA, it will |
| // contain the table descriptor of the root partitioned table. Add that to |
| // the range table in the PlStmt. |
| if (phy_append_dxlop->GetScanId() != gpos::ulong_max) |
| { |
| GPOS_ASSERT(nullptr != phy_append_dxlop->GetDXLTableDesc()); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| (void) ProcessDXLTblDescr(phy_append_dxlop->GetDXLTableDesc(), |
| &base_table_context); |
| |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| append->join_prune_paramids = |
| TranslateJoinPruneParamids(phy_append_dxlop->GetSelectorIds(), |
| oid_type, m_dxl_to_plstmt_context); |
| } |
| |
| // translate children |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| for (ULONG ul = EdxlappendIndexFirstChild; ul < arity; ul++) |
| { |
| CDXLNode *child_dxlnode = (*append_dxlnode)[ul]; |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| GPOS_ASSERT(nullptr != child_plan && "child plan cannot be NULL"); |
| |
| append->appendplans = gpdb::LAppend(append->appendplans, child_plan); |
| } |
| |
| CDXLNode *project_list_dxlnode = (*append_dxlnode)[EdxlappendIndexProjList]; |
| CDXLNode *filter_dxlnode = (*append_dxlnode)[EdxlappendIndexFilter]; |
| |
| plan->targetlist = NIL; |
| const ULONG length = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < length; ++ul) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| GPOS_ASSERT(EdxlopScalarProjectElem == |
| proj_elem_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| CDXLScalarProjElem *sc_proj_elem_dxlop = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| GPOS_ASSERT(1 == proj_elem_dxlnode->Arity()); |
| |
| // translate proj element expression |
| CDXLNode *expr_dxlnode = (*proj_elem_dxlnode)[0]; |
| CDXLScalarIdent *sc_ident_dxlop = |
| CDXLScalarIdent::Cast(expr_dxlnode->GetOperator()); |
| |
| Index idxVarno = OUTER_VAR; |
| AttrNumber attno = (AttrNumber)(ul + 1); |
| |
| Var *var = gpdb::MakeVar( |
| idxVarno, attno, |
| CMDIdGPDB::CastMdid(sc_ident_dxlop->MdidType())->Oid(), |
| sc_ident_dxlop->TypeModifier(), |
| 0 // varlevelsup |
| ); |
| |
| TargetEntry *target_entry = MakeNode(TargetEntry); |
| target_entry->expr = (Expr *) var; |
| target_entry->resname = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| sc_proj_elem_dxlop->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| target_entry->resno = attno; |
| |
| // add column mapping to output translation context |
| output_context->InsertMapping(sc_proj_elem_dxlop->Id(), target_entry); |
| |
| plan->targetlist = gpdb::LAppend(plan->targetlist, target_entry); |
| } |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(output_context); |
| |
| // translate filter |
| plan->qual = TranslateDXLFilterToQual( |
| filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) append; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLMaterialize |
| // |
| // @doc: |
| // Translate DXL materialize node into GPDB Material plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLMaterialize( |
| const CDXLNode *materialize_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create materialize plan node |
| Material *materialize = MakeNode(Material); |
| |
| Plan *plan = &(materialize->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalMaterialize *materialize_dxlop = |
| CDXLPhysicalMaterialize::Cast(materialize_dxlnode->GetOperator()); |
| |
| materialize->cdb_strict = materialize_dxlop->IsEager(); |
| // ensure that executor actually materializes results |
| materialize->cdb_shield_child_from_rescans = true; |
| |
| // translate operator costs |
| TranslatePlanCosts(materialize_dxlnode, plan); |
| |
| // translate materialize child |
| CDXLNode *child_dxlnode = (*materialize_dxlnode)[EdxlmatIndexChild]; |
| |
| CDXLNode *project_list_dxlnode = |
| (*materialize_dxlnode)[EdxlmatIndexProjList]; |
| CDXLNode *filter_dxlnode = (*materialize_dxlnode)[EdxlmatIndexFilter]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, &plan->targetlist, &plan->qual, |
| output_context); |
| |
| plan->lefttree = child_plan; |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) materialize; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLCTEProducerToSharedScan |
| // |
| // @doc: |
| // Translate DXL CTE Producer node into GPDB share input scan plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLCTEProducerToSharedScan( |
| const CDXLNode *cte_producer_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalCTEProducer *cte_prod_dxlop = |
| CDXLPhysicalCTEProducer::Cast(cte_producer_dxlnode->GetOperator()); |
| ULONG cte_id = cte_prod_dxlop->Id(); |
| |
| // create the Share Input Scan representing the CTE Producer |
| ShareInputScan *shared_input_scan = MakeNode(ShareInputScan); |
| shared_input_scan->share_id = cte_id; |
| shared_input_scan->discard_output = true; |
| |
| Plan *plan = &(shared_input_scan->scan.plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| m_dxl_to_plstmt_context->RegisterCTEProducerInfo(cte_id, |
| cte_prod_dxlop->GetOutputColIdxMap(), shared_input_scan); |
| |
| // translate cost of the producer |
| TranslatePlanCosts(cte_producer_dxlnode, plan); |
| |
| // translate child plan |
| CDXLNode *project_list_dxlnode = (*cte_producer_dxlnode)[0]; |
| CDXLNode *child_dxlnode = (*cte_producer_dxlnode)[1]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| GPOS_ASSERT(nullptr != child_plan && "child plan cannot be NULL"); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| // translate proj list |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| plan->lefttree = child_plan; |
| plan->qual = NIL; |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) shared_input_scan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLCTEConsumerToSharedScan |
| // |
| // @doc: |
| // Translate DXL CTE Consumer node into GPDB share input scan plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLCTEConsumerToSharedScan( |
| const CDXLNode *cte_consumer_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray * /*ctxt_translation_prev_siblings*/) |
| { |
| CDXLPhysicalCTEConsumer *cte_consumer_dxlop = |
| CDXLPhysicalCTEConsumer::Cast(cte_consumer_dxlnode->GetOperator()); |
| ULONG cte_id = cte_consumer_dxlop->Id(); |
| ULongPtrArray *output_colidx_map = cte_consumer_dxlop->GetOutputColIdxMap(); |
| |
| // get the producer idx map |
| ULongPtrArray *producer_colidx_map; |
| ShareInputScan *share_input_scan_cte_producer; |
| |
| std::tie(producer_colidx_map, share_input_scan_cte_producer) |
| = m_dxl_to_plstmt_context->GetCTEProducerInfo(cte_id); |
| |
| // init the consumer plan |
| ShareInputScan *share_input_scan_cte_consumer = MakeNode(ShareInputScan); |
| share_input_scan_cte_consumer->share_id = cte_id; |
| share_input_scan_cte_consumer->discard_output = false; |
| |
| Plan *plan = &(share_input_scan_cte_consumer->scan.plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(cte_consumer_dxlnode, plan); |
| |
| #ifdef GPOS_DEBUG |
| ULongPtrArray *output_colids_array = |
| cte_consumer_dxlop->GetOutputColIdsArray(); |
| #endif |
| |
| // generate the target list of the CTE Consumer |
| plan->targetlist = NIL; |
| CDXLNode *project_list_dxlnode = (*cte_consumer_dxlnode)[0]; |
| const ULONG num_of_proj_list_elem = project_list_dxlnode->Arity(); |
| GPOS_ASSERT(num_of_proj_list_elem == output_colids_array->Size()); |
| for (ULONG ul = 0; ul < num_of_proj_list_elem; ul++) |
| { |
| AttrNumber varattno = (AttrNumber)ul + 1; |
| if (output_colidx_map) { |
| ULONG remapping_idx; |
| remapping_idx = *(*output_colidx_map)[ul]; |
| if (producer_colidx_map) { |
| remapping_idx = *(*producer_colidx_map)[remapping_idx]; |
| } |
| GPOS_ASSERT(remapping_idx != gpos::ulong_max); |
| varattno = (AttrNumber)remapping_idx + 1; |
| } |
| |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| CDXLScalarProjElem *sc_proj_elem_dxlop = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| ULONG colid = sc_proj_elem_dxlop->Id(); |
| GPOS_ASSERT(colid == *(*output_colids_array)[ul]); |
| |
| CDXLNode *sc_ident_dxlnode = (*proj_elem_dxlnode)[0]; |
| CDXLScalarIdent *sc_ident_dxlop = |
| CDXLScalarIdent::Cast(sc_ident_dxlnode->GetOperator()); |
| OID oid_type = CMDIdGPDB::CastMdid(sc_ident_dxlop->MdidType())->Oid(); |
| |
| Var *var = |
| gpdb::MakeVar(OUTER_VAR, varattno, oid_type, |
| sc_ident_dxlop->TypeModifier(), 0 /* varlevelsup */); |
| |
| CHAR *resname = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| sc_proj_elem_dxlop->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| TargetEntry *target_entry = gpdb::MakeTargetEntry( |
| (Expr *) var, (AttrNumber)(ul + 1), resname, false /* resjunk */); |
| plan->targetlist = gpdb::LAppend(plan->targetlist, target_entry); |
| |
| output_context->InsertMapping(colid, target_entry); |
| } |
| |
| plan->qual = nullptr; |
| |
| SetParamIds(plan); |
| |
| // DON'T REMOVE, if current consumer need projection, then we can direct add it. |
| // we still keep the path of projection in consumer |
| |
| // Plan *producer_plan = &(share_input_scan_cte_producer->scan.plan); |
| // if (output_colidx_map != nullptr) { |
| // share_input_scan_cte_consumer->need_projection = true; |
| // share_input_scan_cte_consumer->producer_targetlist = gpdb::ListCopy(producer_plan->targetlist); |
| // if (!share_input_scan_cte_consumer->producer_targetlist) { |
| // share_input_scan_cte_consumer->need_projection = false; |
| // } |
| // } |
| |
| return (Plan *) share_input_scan_cte_consumer; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLSequence |
| // |
| // @doc: |
| // Translate DXL sequence node into GPDB Sequence plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLSequence( |
| const CDXLNode *sequence_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create append plan node |
| Sequence *psequence = MakeNode(Sequence); |
| |
| Plan *plan = &(psequence->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(sequence_dxlnode, plan); |
| |
| ULONG arity = sequence_dxlnode->Arity(); |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| for (ULONG ul = 1; ul < arity; ul++) |
| { |
| CDXLNode *child_dxlnode = (*sequence_dxlnode)[ul]; |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| psequence->subplans = gpdb::LAppend(psequence->subplans, child_plan); |
| } |
| |
| CDXLNode *project_list_dxlnode = (*sequence_dxlnode)[0]; |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // base table translation context |
| child_contexts, output_context); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) psequence; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDynTblScan |
| // |
| // @doc: |
| // Translates a DXL dynamic table scan node into a DynamicSeqScan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLDynTblScan( |
| const CDXLNode *dyn_tbl_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray * /*ctxt_translation_prev_siblings*/) |
| { |
| // translate table descriptor into a range table entry |
| CDXLPhysicalDynamicTableScan *dyn_tbl_scan_dxlop = |
| CDXLPhysicalDynamicTableScan::Cast(dyn_tbl_scan_dxlnode->GetOperator()); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| Index index = ProcessDXLTblDescr(dyn_tbl_scan_dxlop->GetDXLTableDescr(), |
| &base_table_context); |
| |
| // create dynamic scan node |
| DynamicSeqScan *dyn_seq_scan = MakeNode(DynamicSeqScan); |
| |
| dyn_seq_scan->seqscan.scanrelid = index; |
| |
| const CDXLTableDescr *dxl_table_descr = |
| dyn_tbl_scan_dxlop->GetDXLTableDescr(); |
| GPOS_ASSERT(dxl_table_descr->LockMode() != -1); |
| |
| dyn_seq_scan->partOids = TranslatePartOids(dyn_tbl_scan_dxlop->GetParts(), |
| dxl_table_descr->LockMode()); |
| |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| |
| const IMDRelation *md_rel = |
| m_md_accessor->RetrieveRel(dxl_table_descr->MDId()); |
| |
| OID oidRel = CMDIdGPDB::CastMdid(md_rel->MDId())->Oid(); |
| |
| dyn_seq_scan->join_prune_paramids = |
| TranslateJoinPruneParamids(dyn_tbl_scan_dxlop->GetSelectorIds(), |
| oid_type, m_dxl_to_plstmt_context); |
| |
| Plan *plan = &(dyn_seq_scan->seqscan.plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| //plan->nMotionNodes = 0; |
| |
| // translate operator costs |
| TranslatePlanCosts(dyn_tbl_scan_dxlnode, plan); |
| |
| GPOS_ASSERT(2 == dyn_tbl_scan_dxlnode->Arity()); |
| |
| // translate proj list and filter |
| CDXLNode *project_list_dxlnode = |
| (*dyn_tbl_scan_dxlnode)[EdxltsIndexProjList]; |
| CDXLNode *filter_dxlnode = (*dyn_tbl_scan_dxlnode)[EdxltsIndexFilter]; |
| |
| // List to hold the quals which contain both security quals and query |
| // quals. |
| List *security_query_quals = NIL; |
| |
| // List to hold the quals after translating filter_dxlnode node. |
| List *query_quals = NIL; |
| |
| // Fetching the RTE of the relation from the rewritten parse tree |
| // based on the oidRel and adding the security quals of the RTE in |
| // the security_query_quals list. |
| AddSecurityQuals(oidRel, &security_query_quals, &index); |
| |
| TranslateProjListAndFilter( |
| project_list_dxlnode, filter_dxlnode, |
| &base_table_context, // translate context for the base table |
| nullptr, // translate_ctxt_left and pdxltrctxRight, |
| &plan->targetlist, &query_quals, output_context); |
| |
| // The security quals should always be executed first when compared to |
| // other quals. So appending query quals to the security_query_quals |
| // list after the security quals. |
| security_query_quals = gpdb::ListConcat(security_query_quals, query_quals); |
| plan->qual = security_query_quals; |
| |
| SetParamIds(plan); |
| |
| return plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDynIdxOnlyScan |
| // |
| // @doc: |
| // Translates a DXL dynamic index scan node into a DynamicIndexOnlyScan |
| // node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLDynIdxOnlyScan( |
| const CDXLNode *dyn_idx_only_scan_dxlnode, |
| CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalDynamicIndexOnlyScan *dyn_index_only_scan_dxlop = |
| CDXLPhysicalDynamicIndexOnlyScan::Cast( |
| dyn_idx_only_scan_dxlnode->GetOperator()); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const CDXLTableDescr *table_desc = |
| dyn_index_only_scan_dxlop->GetDXLTableDescr(); |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel(table_desc->MDId()); |
| |
| Index index = ProcessDXLTblDescr(table_desc, &base_table_context); |
| |
| DynamicIndexOnlyScan *dyn_idx_only_scan = MakeNode(DynamicIndexOnlyScan); |
| |
| dyn_idx_only_scan->indexscan.scan.scanrelid = index; |
| |
| dyn_idx_only_scan->partOids = TranslatePartOids( |
| dyn_index_only_scan_dxlop->GetParts(), table_desc->LockMode()); |
| |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| dyn_idx_only_scan->join_prune_paramids = |
| TranslateJoinPruneParamids(dyn_index_only_scan_dxlop->GetSelectorIds(), |
| oid_type, m_dxl_to_plstmt_context); |
| |
| CMDIdGPDB *mdid_index = CMDIdGPDB::CastMdid( |
| dyn_index_only_scan_dxlop->GetDXLIndexDescr()->MDId()); |
| const IMDIndex *md_index = m_md_accessor->RetrieveIndex(mdid_index); |
| Oid index_oid = mdid_index->Oid(); |
| |
| GPOS_ASSERT(InvalidOid != index_oid); |
| dyn_idx_only_scan->indexscan.indexid = index_oid; |
| |
| Plan *plan = &(dyn_idx_only_scan->indexscan.scan.plan); |
| |
| CDXLTranslateContextBaseTable index_context(m_mp); |
| |
| // translate index targetlist |
| dyn_idx_only_scan->indexscan.indextlist = TranslateDXLIndexTList( |
| md_rel, md_index, index, table_desc, &index_context); |
| |
| TranslatePlan(plan, dyn_idx_only_scan_dxlnode, output_context, |
| m_dxl_to_plstmt_context, &index_context, |
| ctxt_translation_prev_siblings); |
| |
| dyn_idx_only_scan->indexscan.indexorderdir = |
| CTranslatorUtils::GetScanDirection( |
| dyn_index_only_scan_dxlop->GetIndexScanDir()); |
| |
| // translate index condition list |
| List *index_cond = NIL; |
| List *index_orig_cond = NIL; |
| |
| TranslateIndexConditions( |
| (*dyn_idx_only_scan_dxlnode) |
| [CDXLPhysicalDynamicIndexScan::EdxldisIndexCondition], |
| dyn_index_only_scan_dxlop->GetDXLTableDescr(), |
| false, // is_bitmap_index_probe |
| md_index, md_rel, output_context, &base_table_context, |
| ctxt_translation_prev_siblings, &index_cond, &index_orig_cond); |
| |
| |
| dyn_idx_only_scan->indexscan.indexqual = index_cond; |
| |
| SetParamIds(plan); |
| |
| return (Plan *) dyn_idx_only_scan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDynIdxScan |
| // |
| // @doc: |
| // Translates a DXL dynamic index scan node into a DynamicIndexScan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLDynIdxScan( |
| const CDXLNode *dyn_idx_only_scan_dxlnode, |
| CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalDynamicIndexScan *dyn_index_scan_dxlop = |
| CDXLPhysicalDynamicIndexScan::Cast( |
| dyn_idx_only_scan_dxlnode->GetOperator()); |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const CDXLTableDescr *table_desc = dyn_index_scan_dxlop->GetDXLTableDescr(); |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel(table_desc->MDId()); |
| |
| Index index = ProcessDXLTblDescr(table_desc, &base_table_context); |
| |
| DynamicIndexScan *dyn_idx_only_scan = MakeNode(DynamicIndexScan); |
| |
| dyn_idx_only_scan->indexscan.scan.scanrelid = index; |
| |
| GPOS_ASSERT(table_desc->LockMode() != -1); |
| |
| dyn_idx_only_scan->partOids = TranslatePartOids( |
| dyn_index_scan_dxlop->GetParts(), table_desc->LockMode()); |
| |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| dyn_idx_only_scan->join_prune_paramids = |
| TranslateJoinPruneParamids(dyn_index_scan_dxlop->GetSelectorIds(), |
| oid_type, m_dxl_to_plstmt_context); |
| |
| CMDIdGPDB *mdid_index = |
| CMDIdGPDB::CastMdid(dyn_index_scan_dxlop->GetDXLIndexDescr()->MDId()); |
| const IMDIndex *md_index = m_md_accessor->RetrieveIndex(mdid_index); |
| Oid index_oid = mdid_index->Oid(); |
| |
| GPOS_ASSERT(InvalidOid != index_oid); |
| dyn_idx_only_scan->indexscan.indexid = index_oid; |
| |
| Plan *plan = &(dyn_idx_only_scan->indexscan.scan.plan); |
| |
| TranslatePlan(plan, dyn_idx_only_scan_dxlnode, output_context, |
| m_dxl_to_plstmt_context, &base_table_context, |
| ctxt_translation_prev_siblings); |
| |
| dyn_idx_only_scan->indexscan.indexorderdir = |
| CTranslatorUtils::GetScanDirection( |
| dyn_index_scan_dxlop->GetIndexScanDir()); |
| |
| // translate index condition list |
| List *index_cond = NIL; |
| List *index_orig_cond = NIL; |
| |
| TranslateIndexConditions( |
| (*dyn_idx_only_scan_dxlnode) |
| [CDXLPhysicalDynamicIndexScan::EdxldisIndexCondition], |
| dyn_index_scan_dxlop->GetDXLTableDescr(), |
| false, // is_bitmap_index_probe |
| md_index, md_rel, output_context, &base_table_context, |
| ctxt_translation_prev_siblings, &index_cond, &index_orig_cond); |
| |
| |
| dyn_idx_only_scan->indexscan.indexqual = index_cond; |
| dyn_idx_only_scan->indexscan.indexqualorig = index_orig_cond; |
| |
| SetParamIds(plan); |
| |
| return (Plan *) dyn_idx_only_scan; |
| } |
| |
| // remaps varnos qual and targetlist from one tuple descriptor to another. |
| // eg: remap varnos from a root partition to a child partition, or vice-versa |
| static TupleDesc |
| RemapAttrsFromTupDesc(TupleDesc fromDesc, TupleDesc toDesc, Index index, |
| List *qual, List *targetlist) |
| { |
| AttrMap *attMap; |
| attMap = build_attrmap_by_name_if_req(toDesc, fromDesc); |
| |
| /* If attribute remapping is not necessary, then do not change the varattno */ |
| if (attMap) |
| { |
| change_varattnos_of_a_varno((Node *) qual, attMap->attnums, index); |
| change_varattnos_of_a_varno((Node *) targetlist, attMap->attnums, index); |
| fromDesc = toDesc; |
| free_attrmap(attMap); |
| } |
| return fromDesc; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @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; |
| // create dynamic scan node |
| DynamicForeignScan *dyn_foreign_scan = MakeNode(DynamicForeignScan); |
| |
| 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); |
| } |
| |
| 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); |
| |
| GPOS_ASSERT(2 == dyn_foreign_scan_dxlnode->Arity()); |
| |
| // 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()); |
| |
| 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); |
| |
| 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; |
| |
| // 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); |
| |
| // set the rte relid back to the root |
| rte->relid = oid_root; |
| // translate operator costs |
| TranslatePlanCosts(dyn_foreign_scan_dxlnode, plan); |
| |
| SetParamIds(plan); |
| |
| return plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDml |
| // |
| // @doc: |
| // Translates a DXL DML node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLDml( |
| const CDXLNode *dml_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // translate table descriptor into a range table entry |
| CDXLPhysicalDML *phy_dml_dxlop = |
| CDXLPhysicalDML::Cast(dml_dxlnode->GetOperator()); |
| |
| // create ModifyTable node |
| ModifyTable *dml = MakeNode(ModifyTable); |
| Plan *plan = &(dml->plan); |
| BOOL isSplit = phy_dml_dxlop->FSplit(); |
| List *updateCols = NIL; |
| |
| switch (phy_dml_dxlop->GetDmlOpType()) |
| { |
| case gpdxl::Edxldmldelete: |
| { |
| m_cmd_type = CMD_DELETE; |
| break; |
| } |
| case gpdxl::Edxldmlupdate: |
| { |
| m_cmd_type = CMD_UPDATE; |
| break; |
| } |
| case gpdxl::Edxldmlinsert: |
| { |
| m_cmd_type = CMD_INSERT; |
| break; |
| } |
| case gpdxl::EdxldmlSentinel: |
| default: |
| { |
| GPOS_RAISE( |
| gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtConversion, |
| GPOS_WSZ_LIT("Unexpected error during plan generation.")); |
| break; |
| } |
| } |
| |
| IMDId *mdid_target_table = phy_dml_dxlop->GetDXLTableDescr()->MDId(); |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel(mdid_target_table); |
| |
| if (md_rel->IsNonBlockTable()) |
| { |
| isSplit = true; // AO tables are always use split updates |
| } |
| |
| if (md_rel->IsPartitioned()) |
| { |
| dml->forceTupleRouting = true; |
| } |
| else |
| { |
| dml->forceTupleRouting = false; |
| } |
| |
| if (IMDRelation::EreldistrMasterOnly != md_rel->GetRelDistribution()) |
| { |
| m_is_tgt_tbl_distributed = true; |
| } |
| |
| if (CMD_UPDATE == m_cmd_type && |
| gpdb::HasUpdateTriggers(CMDIdGPDB::CastMdid(mdid_target_table)->Oid())) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, |
| GPOS_WSZ_LIT("UPDATE on a table with UPDATE triggers")); |
| } |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| CDXLTableDescr *table_descr = phy_dml_dxlop->GetDXLTableDescr(); |
| |
| Index index = ProcessDXLTblDescr(table_descr, &base_table_context); |
| |
| m_result_rel_list = gpdb::LAppendInt(m_result_rel_list, index); |
| |
| CDXLNode *project_list_dxlnode = (*dml_dxlnode)[0]; |
| CDXLNode *child_dxlnode = (*dml_dxlnode)[1]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| List *dml_target_list = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| BOOL isNonSplitUpdate = (m_cmd_type == CMD_UPDATE && !isSplit); |
| if (isNonSplitUpdate) |
| { |
| // set all columns as updateCols for non-split updates |
| updateCols = GetRelationActiveColums(md_rel); |
| } |
| |
| // project all columns for intermediate (mid-level) partitions, as we need to pass through the partition keys |
| // but do not have that information for intermediate partitions during Orca's optimization |
| BOOL is_intermediate_part = |
| (md_rel->IsPartitioned() && nullptr != md_rel->MDPartConstraint()); |
| if (m_cmd_type != CMD_DELETE || is_intermediate_part) |
| { |
| // pad child plan's target list with NULLs for dropped columns for UPDATE/INSERTs and for DELETEs on intermeidate partitions |
| dml_target_list = |
| CreateTargetListWithNullsForDroppedCols(dml_target_list, md_rel, !isNonSplitUpdate); |
| } |
| |
| // Add junk columns to the target list for the 'action', 'ctid', |
| // 'gp_segment_id'. The ModifyTable node will find these based |
| // on the resnames. |
| if (m_cmd_type == CMD_UPDATE && isSplit) |
| { |
| (void) AddJunkTargetEntryForColId(&dml_target_list, &child_context, |
| phy_dml_dxlop->ActionColId(), |
| "DMLAction"); |
| } |
| |
| if (m_cmd_type == CMD_UPDATE || m_cmd_type == CMD_DELETE) |
| { |
| AddJunkTargetEntryForColId(&dml_target_list, &child_context, |
| phy_dml_dxlop->GetCtIdColId(), "ctid"); |
| AddJunkTargetEntryForColId(&dml_target_list, &child_context, |
| phy_dml_dxlop->GetSegmentIdColId(), |
| "gp_segment_id"); |
| } |
| |
| // Add a Result node on top of the child plan, to coerce the target |
| // list to match the exact physical layout of the target table, |
| // including dropped columns. Often, the Result node isn't really |
| // needed, as the child node could do the projection, but we don't have |
| // the information to determine that here. There's a step in the |
| // backend optimize_query() function to eliminate unnecessary Results |
| // through the plan, hopefully this Result gets eliminated there. |
| Result *result = MakeNode(Result); |
| Plan *result_plan = &(result->plan); |
| |
| result_plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| result_plan->lefttree = child_plan; |
| |
| result_plan->targetlist = dml_target_list; |
| SetParamIds(result_plan); |
| |
| if (m_cmd_type == CMD_UPDATE && isSplit) |
| { |
| Result *final_result = MakeNode(Result); |
| Plan *final_result_plan = &(final_result->plan); |
| |
| final_result_plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| final_result_plan->lefttree = result_plan; |
| final_result_plan->targetlist = CreateDirectCopyTargetList(dml_target_list); |
| final_result->resconstantqual = |
| (Node *) gpdb::LAppend(NIL, gpdb::MakeBoolConst(true /*value*/, false /*isnull*/)); |
| |
| SetParamIds(final_result_plan); |
| |
| result = final_result; |
| result_plan = final_result_plan; |
| } |
| |
| child_plan = (Plan *) result; |
| |
| dml->operation = m_cmd_type; |
| dml->canSetTag = true; // FIXME |
| dml->nominalRelation = index; |
| dml->resultRelations = ListMake1Int(index); |
| // GPDB_14_MERGE_FIXME: fix split update and missing partColsUpdated |
| dml->rootRelation = md_rel->IsPartitioned() ? index : 0; |
| //dml->plans = ListMake1(child_plan); |
| dml->updateColnosLists = ListMake1(updateCols); |
| |
| dml->fdwPrivLists = ListMake1(NIL); |
| |
| // ORCA plans all updates as split updates |
| if (m_cmd_type == CMD_UPDATE) |
| { |
| dml->splitUpdate = isSplit; |
| } |
| |
| plan->lefttree = child_plan; |
| plan->righttree = NULL; |
| plan->targetlist = NIL; |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| SetParamIds(plan); |
| |
| if (m_is_tgt_tbl_distributed) |
| { |
| PlanSlice *current_slice = m_dxl_to_plstmt_context->GetCurrentSlice(); |
| current_slice->numsegments = m_num_of_segments; |
| current_slice->gangType = GANGTYPE_PRIMARY_WRITER; |
| } |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| // translate operator costs |
| TranslatePlanCosts(dml_dxlnode, plan); |
| |
| return (Plan *) dml; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLDirectDispatchInfo |
| // |
| // @doc: |
| // Translate the direct dispatch info |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLDirectDispatchInfo( |
| CDXLDirectDispatchInfo *dxl_direct_dispatch_info, |
| RangeTblEntry *pRTEHashFuncCal) |
| { |
| if (!optimizer_enable_direct_dispatch || |
| nullptr == dxl_direct_dispatch_info) |
| { |
| return NIL; |
| } |
| |
| CDXLDatum2dArray *dispatch_identifier_datum_arrays = |
| dxl_direct_dispatch_info->GetDispatchIdentifierDatumArray(); |
| |
| if (dispatch_identifier_datum_arrays == nullptr || |
| 0 == dispatch_identifier_datum_arrays->Size()) |
| { |
| return NIL; |
| } |
| |
| CDXLDatumArray *dxl_datum_array = (*dispatch_identifier_datum_arrays)[0]; |
| GPOS_ASSERT(0 < dxl_datum_array->Size()); |
| |
| const ULONG length = dispatch_identifier_datum_arrays->Size(); |
| |
| if (dxl_direct_dispatch_info->FContainsRawValues()) |
| { |
| List *segids_list = NIL; |
| INT segid; |
| Const *const_expr = nullptr; |
| |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CDXLDatumArray *dispatch_identifier_datum_array = |
| (*dispatch_identifier_datum_arrays)[ul]; |
| GPOS_ASSERT(1 == dispatch_identifier_datum_array->Size()); |
| const_expr = |
| (Const *) m_translator_dxl_to_scalar->TranslateDXLDatumToScalar( |
| (*dispatch_identifier_datum_array)[0]); |
| |
| segid = DatumGetInt32(const_expr->constvalue); |
| if (segid >= -1 && segid < (INT) m_num_of_segments) |
| { |
| segids_list = gpdb::LAppendInt(segids_list, segid); |
| } |
| } |
| |
| if (segids_list == NIL && const_expr) |
| { |
| // If no valid segids were found, and there were items in the |
| // dispatch identifier array, then append the last item to behave |
| // in same manner as Planner for consistency. Currently this will |
| // lead to a FATAL in the backend when we dispatch. |
| segids_list = gpdb::LAppendInt(segids_list, segid); |
| } |
| return segids_list; |
| } |
| |
| ULONG hash_code = GetDXLDatumGPDBHash(dxl_datum_array, pRTEHashFuncCal); |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CDXLDatumArray *dispatch_identifier_datum_array = |
| (*dispatch_identifier_datum_arrays)[ul]; |
| GPOS_ASSERT(0 < dispatch_identifier_datum_array->Size()); |
| ULONG hash_code_new = GetDXLDatumGPDBHash( |
| dispatch_identifier_datum_array, pRTEHashFuncCal); |
| |
| if (hash_code != hash_code_new) |
| { |
| // values don't hash to the same segment |
| return NIL; |
| } |
| } |
| |
| List *segids_list = gpdb::LAppendInt(NIL, hash_code); |
| return segids_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::GetDXLDatumGPDBHash |
| // |
| // @doc: |
| // Hash a DXL datum |
| // |
| //--------------------------------------------------------------------------- |
| ULONG |
| CTranslatorDXLToPlStmt::GetDXLDatumGPDBHash(CDXLDatumArray *dxl_datum_array, |
| RangeTblEntry *pRTEHashFuncCal) |
| { |
| List *consts_list = NIL; |
| Oid *hashfuncs; |
| |
| const ULONG length = dxl_datum_array->Size(); |
| |
| if (pRTEHashFuncCal != nullptr) |
| { |
| // If we have one unique RTE in FROM clause, |
| // then we do direct dispatch based on the distribution policy |
| |
| gpdb::RelationWrapper rel = gpdb::GetRelation(pRTEHashFuncCal->relid); |
| GPOS_ASSERT(rel); |
| GpPolicy *policy = rel->rd_cdbpolicy; |
| int policy_nattrs = policy->nattrs; |
| TupleDesc desc = rel->rd_att; |
| Oid *opclasses = policy->opclasses; |
| hashfuncs = (Oid *) gpdb::GPDBAlloc(policy_nattrs * sizeof(Oid)); |
| |
| for (int i = 0; i < policy_nattrs; i++) |
| { |
| AttrNumber attnum = policy->attrs[i]; |
| Oid typeoid = desc->attrs[attnum - 1].atttypid; |
| Oid opfamily; |
| |
| opfamily = gpdb::GetOpclassFamily(opclasses[i]); |
| hashfuncs[i] = gpdb::GetHashProcInOpfamily(opfamily, typeoid); |
| } |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CDXLDatum *datum_dxl = (*dxl_datum_array)[ul]; |
| Const *const_expr = |
| (Const *) m_translator_dxl_to_scalar->TranslateDXLDatumToScalar( |
| datum_dxl); |
| consts_list = gpdb::LAppend(consts_list, const_expr); |
| } |
| } |
| else |
| { |
| // If we have multiple tables in the "from" clause, |
| // we calculate hashfunction based on the consttype |
| |
| hashfuncs = (Oid *) gpdb::GPDBAlloc(length * sizeof(Oid)); |
| for (ULONG ul = 0; ul < length; ul++) |
| { |
| CDXLDatum *datum_dxl = (*dxl_datum_array)[ul]; |
| |
| Const *const_expr = |
| (Const *) m_translator_dxl_to_scalar->TranslateDXLDatumToScalar( |
| datum_dxl); |
| consts_list = gpdb::LAppend(consts_list, const_expr); |
| hashfuncs[ul] = |
| m_dxl_to_plstmt_context->GetDistributionHashFuncForType( |
| const_expr->consttype); |
| } |
| } |
| |
| ULONG hash = |
| gpdb::CdbHashConstList(consts_list, m_num_of_segments, hashfuncs); |
| |
| gpdb::ListFreeDeep(consts_list); |
| gpdb::GPDBFree(hashfuncs); |
| |
| return hash; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLSplit |
| // |
| // @doc: |
| // Translates a DXL Split node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLSplit( |
| const CDXLNode *split_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalSplit *phy_split_dxlop = |
| CDXLPhysicalSplit::Cast(split_dxlnode->GetOperator()); |
| |
| // create SplitUpdate node |
| SplitUpdate *split = MakeNode(SplitUpdate); |
| Plan *plan = &(split->plan); |
| |
| CDXLNode *project_list_dxlnode = (*split_dxlnode)[0]; |
| CDXLNode *child_dxlnode = (*split_dxlnode)[1]; |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list and filter |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| // translate delete and insert columns |
| ULongPtrArray *deletion_colid_array = |
| phy_split_dxlop->GetDeletionColIdArray(); |
| ULongPtrArray *insertion_colid_array = |
| phy_split_dxlop->GetInsertionColIdArray(); |
| |
| GPOS_ASSERT(insertion_colid_array->Size() == deletion_colid_array->Size()); |
| |
| split->deleteColIdx = CTranslatorUtils::ConvertColidToAttnos( |
| deletion_colid_array, &child_context); |
| split->insertColIdx = CTranslatorUtils::ConvertColidToAttnos( |
| insertion_colid_array, &child_context); |
| |
| const TargetEntry *te_action_col = |
| output_context->GetTargetEntry(phy_split_dxlop->ActionColId()); |
| |
| if (nullptr == te_action_col) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| phy_split_dxlop->ActionColId()); |
| } |
| |
| split->actionColIdx = te_action_col->resno; |
| |
| plan->lefttree = child_plan; |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| // translate operator costs |
| TranslatePlanCosts(split_dxlnode, plan); |
| |
| return (Plan *) split; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLAssert |
| // |
| // @doc: |
| // Translate DXL assert node into GPDB assert plan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLAssert( |
| const CDXLNode *assert_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| // create assert plan node |
| AssertOp *assert_node = MakeNode(AssertOp); |
| |
| Plan *plan = &(assert_node->plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| CDXLPhysicalAssert *assert_dxlop = |
| CDXLPhysicalAssert::Cast(assert_dxlnode->GetOperator()); |
| |
| // translate error code into the its internal GPDB representation |
| const CHAR *error_code = assert_dxlop->GetSQLState(); |
| GPOS_ASSERT(GPOS_SQLSTATE_LENGTH == clib::Strlen(error_code)); |
| |
| assert_node->errcode = |
| MAKE_SQLSTATE(error_code[0], error_code[1], error_code[2], |
| error_code[3], error_code[4]); |
| CDXLNode *filter_dxlnode = |
| (*assert_dxlnode)[CDXLPhysicalAssert::EdxlassertIndexFilter]; |
| |
| assert_node->errmessage = |
| CTranslatorUtils::GetAssertErrorMsgs(filter_dxlnode); |
| |
| // translate operator costs |
| TranslatePlanCosts(assert_dxlnode, plan); |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| // translate child plan |
| CDXLNode *child_dxlnode = |
| (*assert_dxlnode)[CDXLPhysicalAssert::EdxlassertIndexChild]; |
| Plan *child_plan = TranslateDXLOperatorToPlan( |
| child_dxlnode, &child_context, ctxt_translation_prev_siblings); |
| |
| GPOS_ASSERT(nullptr != child_plan && "child plan cannot be NULL"); |
| |
| assert_node->plan.lefttree = child_plan; |
| |
| CDXLNode *project_list_dxlnode = |
| (*assert_dxlnode)[CDXLPhysicalAssert::EdxlassertIndexProjList]; |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| // translate proj list |
| plan->targetlist = |
| TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // translate context for the base table |
| child_contexts, output_context); |
| |
| // translate assert constraints |
| plan->qual = TranslateDXLAssertConstraints(filter_dxlnode, output_context, |
| child_contexts); |
| |
| GPOS_ASSERT(gpdb::ListLength(plan->qual) == |
| gpdb::ListLength(assert_node->errmessage)); |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| return (Plan *) assert_node; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::ProcessDXLTblDescr |
| // |
| // @doc: |
| // Translates a DXL table descriptor into a range table entry and stores |
| // it in m_dxl_to_plstmt_context if it's needed (in case of DML operations |
| // there is more than one table descriptors which point to the result |
| // relation, so if rte was alredy translated, this rte will be updated and |
| // index of this rte at m_dxl_to_plstmt_context->m_rtable_entries_list |
| // (shortened as "rte_list"), will be returned, if the rte wasn't |
| // translated, the newly created rte will be appended to rte_list and it's |
| // index returned). Also this function fills base_table_context for the |
| // mapping from colids to index attnos instead of table attnos. |
| // Returns index of translated range table entry at the rte_list. |
| // |
| //--------------------------------------------------------------------------- |
| Index |
| CTranslatorDXLToPlStmt::ProcessDXLTblDescr( |
| const CDXLTableDescr *table_descr, |
| CDXLTranslateContextBaseTable *base_table_context) |
| { |
| GPOS_ASSERT(nullptr != table_descr); |
| |
| BOOL rte_was_translated = false; |
| |
| ULONG assigned_query_id = table_descr->GetAssignedQueryIdForTargetRel(); |
| Index index = m_dxl_to_plstmt_context->GetRTEIndexByAssignedQueryId( |
| assigned_query_id, &rte_was_translated); |
| |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel(table_descr->MDId()); |
| const ULONG num_of_non_sys_cols = |
| CTranslatorUtils::GetNumNonSystemColumns(md_rel); |
| |
| // get oid for table |
| Oid oid = CMDIdGPDB::CastMdid(table_descr->MDId())->Oid(); |
| GPOS_ASSERT(InvalidOid != oid); |
| |
| // save oid and range index in translation context |
| base_table_context->SetOID(oid); |
| base_table_context->SetRelIndex(index); |
| |
| // save mapping col id -> index in translate context |
| const ULONG arity = table_descr->Arity(); |
| for (ULONG ul = 0; ul < arity; ++ul) |
| { |
| const CDXLColDescr *dxl_col_descr = table_descr->GetColumnDescrAt(ul); |
| GPOS_ASSERT(nullptr != dxl_col_descr); |
| |
| INT attno = dxl_col_descr->AttrNum(); |
| GPOS_ASSERT(0 != attno); |
| |
| (void) base_table_context->InsertMapping(dxl_col_descr->Id(), attno); |
| } |
| |
| ULONG acl_mode = table_descr->GetAclMode(); |
| GPOS_ASSERT(acl_mode <= std::numeric_limits<AclMode>::max()); |
| AclMode required_perms = static_cast<AclMode>(acl_mode); |
| |
| // descriptor was already processed, and translated RTE is stored at |
| // context rtable list (only update required perms of this rte is needed) |
| if (rte_was_translated) |
| { |
| RangeTblEntry *rte = m_dxl_to_plstmt_context->GetRTEByIndex(index); |
| GPOS_ASSERT(nullptr != rte); |
| rte->requiredPerms |= required_perms; |
| return index; |
| } |
| |
| // create a new RTE (and it's alias) and store it at context rtable list |
| RangeTblEntry *rte = MakeNode(RangeTblEntry); |
| rte->rtekind = RTE_RELATION; |
| rte->relid = oid; |
| rte->checkAsUser = table_descr->GetExecuteAsUserId(); |
| rte->requiredPerms |= required_perms; |
| rte->rellockmode = table_descr->LockMode(); |
| |
| Alias *alias = MakeNode(Alias); |
| alias->colnames = NIL; |
| |
| // get table alias |
| alias->aliasname = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| table_descr->MdName()->GetMDName()->GetBuffer()); |
| |
| // get column names |
| INT last_attno = 0; |
| for (ULONG ul = 0; ul < arity; ++ul) |
| { |
| const CDXLColDescr *dxl_col_descr = table_descr->GetColumnDescrAt(ul); |
| INT attno = dxl_col_descr->AttrNum(); |
| |
| if (0 < attno) |
| { |
| // if attno > last_attno + 1, there were dropped attributes |
| // add those to the RTE as they are required by GPDB |
| for (INT dropped_col_attno = last_attno + 1; |
| dropped_col_attno < attno; dropped_col_attno++) |
| { |
| Value *val_dropped_colname = gpdb::MakeStringValue(PStrDup("")); |
| alias->colnames = |
| gpdb::LAppend(alias->colnames, val_dropped_colname); |
| } |
| |
| // non-system attribute |
| CHAR *col_name_char_array = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| dxl_col_descr->MdName()->GetMDName()->GetBuffer()); |
| Value *val_colname = gpdb::MakeStringValue(col_name_char_array); |
| |
| alias->colnames = gpdb::LAppend(alias->colnames, val_colname); |
| last_attno = attno; |
| } |
| } |
| |
| // if there are any dropped columns at the end, add those too to the RangeTblEntry |
| for (ULONG ul = last_attno + 1; ul <= num_of_non_sys_cols; ul++) |
| { |
| Value *val_dropped_colname = gpdb::MakeStringValue(PStrDup("")); |
| alias->colnames = gpdb::LAppend(alias->colnames, val_dropped_colname); |
| } |
| |
| rte->eref = alias; |
| rte->alias = alias; |
| |
| // A new RTE is added to the range table entries list if it's not found in the look |
| // up table. However, it is only added to the look up table if it's a result relation |
| // This is because the look up table is our way of merging duplicate result relations |
| m_dxl_to_plstmt_context->AddRTE(rte); |
| GPOS_ASSERT(gpdb::ListLength( |
| m_dxl_to_plstmt_context->GetRTableEntriesList()) == index); |
| if (UNASSIGNED_QUERYID != assigned_query_id) |
| { |
| m_dxl_to_plstmt_context->InsertUsedRTEIndexes(assigned_query_id, index); |
| } |
| |
| return index; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // update_unknown_locale_walker |
| // |
| // @doc: |
| // Given an expression tree and a TargetEntry pointer context, look for a |
| // matching target entry in the expression tree and overwrite the given |
| // TargetEntry context's resname with the original found in the expression |
| // tree. |
| // |
| //--------------------------------------------------------------------------- |
| static bool |
| update_unknown_locale_walker(Node *node, void *context) |
| { |
| if (node == nullptr) |
| { |
| return false; |
| } |
| |
| TargetEntry *unknown_target_entry = (TargetEntry *) context; |
| |
| if (IsA(node, TargetEntry)) |
| { |
| TargetEntry *te = (TargetEntry *) node; |
| |
| if (te->resorigtbl == unknown_target_entry->resorigtbl && |
| te->resno == unknown_target_entry->resno) |
| { |
| unknown_target_entry->resname = te->resname; |
| return false; |
| } |
| } |
| else if (IsA(node, Query)) |
| { |
| Query *query = (Query *) node; |
| |
| return gpdb::WalkExpressionTree( |
| (Node *) query->targetList, |
| (bool (*)(Node *, void *)) update_unknown_locale_walker, (void *) context); |
| } |
| |
| return gpdb::WalkExpressionTree( |
| node, (bool (*)(Node *, void *)) update_unknown_locale_walker, (void *) context); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLProjList |
| // |
| // @doc: |
| // Translates a DXL projection list node into a target list. |
| // For base table projection lists, the caller should provide a base table |
| // translation context with table oid, rtable index and mappings for the columns. |
| // For other nodes translate_ctxt_left and pdxltrctxRight give |
| // the mappings of column ids to target entries in the corresponding child nodes |
| // for resolving the origin of the target entries |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLProjList( |
| const CDXLNode *project_list_dxlnode, |
| const CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *child_contexts, |
| CDXLTranslateContext *output_context) |
| { |
| if (nullptr == project_list_dxlnode) |
| { |
| return nullptr; |
| } |
| |
| List *target_list = NIL; |
| |
| // translate each DXL project element into a target entry |
| const ULONG arity = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ++ul) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| GPOS_ASSERT(EdxlopScalarProjectElem == |
| proj_elem_dxlnode->GetOperator()->GetDXLOperator()); |
| CDXLScalarProjElem *sc_proj_elem_dxlop = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| GPOS_ASSERT(1 == proj_elem_dxlnode->Arity()); |
| |
| // translate proj element expression |
| CDXLNode *expr_dxlnode = (*proj_elem_dxlnode)[0]; |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, base_table_context, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| Expr *expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| expr_dxlnode, &colid_var_mapping); |
| |
| GPOS_ASSERT(nullptr != expr); |
| |
| TargetEntry *target_entry = MakeNode(TargetEntry); |
| target_entry->expr = expr; |
| target_entry->resname = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| sc_proj_elem_dxlop->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| target_entry->resno = (AttrNumber)(ul + 1); |
| |
| if (IsA(expr, Var)) |
| { |
| // check the origin of the left or the right side |
| // of the current operator and if it is derived from a base relation, |
| // set resorigtbl and resorigcol appropriately |
| |
| if (nullptr != base_table_context) |
| { |
| // translating project list of a base table |
| target_entry->resorigtbl = base_table_context->GetOid(); |
| target_entry->resorigcol = ((Var *) expr)->varattno; |
| } |
| else |
| { |
| // not translating a base table proj list: variable must come from |
| // the left or right child of the operator |
| |
| GPOS_ASSERT(nullptr != child_contexts); |
| GPOS_ASSERT(0 != child_contexts->Size()); |
| ULONG colid = CDXLScalarIdent::Cast(expr_dxlnode->GetOperator()) |
| ->GetDXLColRef() |
| ->Id(); |
| |
| const CDXLTranslateContext *translate_ctxt_left = |
| (*child_contexts)[0]; |
| GPOS_ASSERT(nullptr != translate_ctxt_left); |
| const TargetEntry *pteOriginal = |
| translate_ctxt_left->GetTargetEntry(colid); |
| |
| if (nullptr == pteOriginal) |
| { |
| // variable not found on the left side |
| GPOS_ASSERT(2 == child_contexts->Size()); |
| const CDXLTranslateContext *pdxltrctxRight = |
| (*child_contexts)[1]; |
| |
| GPOS_ASSERT(nullptr != pdxltrctxRight); |
| pteOriginal = pdxltrctxRight->GetTargetEntry(colid); |
| } |
| |
| if (nullptr == pteOriginal) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, |
| gpdxl::ExmiDXL2PlStmtAttributeNotFound, colid); |
| } |
| target_entry->resorigtbl = pteOriginal->resorigtbl; |
| target_entry->resorigcol = pteOriginal->resorigcol; |
| |
| // ORCA represents strings using wide characters. That can |
| // require converting from multibyte characters using |
| // vswprintf(). However, vswprintf() is dependent on the system |
| // locale which is set at the database level. When that locale |
| // cannot interpret the string correctly, it fails. ORCA |
| // bypasses the failure by using a generic "UNKNOWN" string. |
| // When that happens, the following code translates it back to |
| // the original multibyte string. |
| if (strcmp(target_entry->resname, "UNKNOWN") == 0) |
| { |
| update_unknown_locale_walker( |
| (Node *) output_context->GetQuery(), |
| (void *) target_entry); |
| } |
| } |
| } |
| |
| // add column mapping to output translation context |
| output_context->InsertMapping(sc_proj_elem_dxlop->Id(), target_entry); |
| |
| target_list = gpdb::LAppend(target_list, target_entry); |
| } |
| |
| return target_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::CreateTargetListWithNullsForDroppedCols |
| // |
| // @doc: |
| // Construct the target list for a DML statement by adding NULL elements |
| // for dropped columns |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::CreateTargetListWithNullsForDroppedCols( |
| List *target_list, const IMDRelation *md_rel, bool keepDropedAsNull) |
| { |
| // There are cases where target list can be null |
| // Eg. insert rows with no columns into a table with no columns |
| // |
| // create table foo(); |
| // insert into foo default values; |
| if (nullptr == target_list) |
| { |
| return nullptr; |
| } |
| |
| GPOS_ASSERT(gpdb::ListLength(target_list) <= md_rel->ColumnCount()); |
| |
| List *result_list = NIL; |
| ULONG last_tgt_elem = 0; |
| ULONG resno = 1; |
| |
| const ULONG num_of_rel_cols = md_rel->ColumnCount(); |
| |
| for (ULONG ul = 0; ul < num_of_rel_cols; ul++) |
| { |
| const IMDColumn *md_col = md_rel->GetMdCol(ul); |
| |
| if (md_col->IsSystemColumn()) |
| { |
| continue; |
| } |
| |
| Expr *expr = nullptr; |
| if (md_col->IsDropped()) |
| { |
| if (!keepDropedAsNull) |
| { |
| continue; |
| } |
| |
| // add a NULL element |
| OID oid_type = CMDIdGPDB::CastMdid( |
| m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| |
| expr = (Expr *) gpdb::MakeNULLConst(oid_type); |
| } |
| else |
| { |
| TargetEntry *target_entry = |
| (TargetEntry *) gpdb::ListNth(target_list, last_tgt_elem); |
| expr = (Expr *) gpdb::CopyObject(target_entry->expr); |
| last_tgt_elem++; |
| } |
| |
| CHAR *name_str = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| md_col->Mdname().GetMDName()->GetBuffer()); |
| TargetEntry *te_new = |
| gpdb::MakeTargetEntry(expr, resno, name_str, false /*resjunk*/); |
| result_list = gpdb::LAppend(result_list, te_new); |
| resno++; |
| } |
| |
| return result_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLProjectListToHashTargetList |
| // |
| // @doc: |
| // Create a target list for the hash node of a hash join plan node by creating a list |
| // of references to the elements in the child project list |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLProjectListToHashTargetList( |
| const CDXLNode *project_list_dxlnode, CDXLTranslateContext *child_context, |
| CDXLTranslateContext *output_context) |
| { |
| List *target_list = NIL; |
| const ULONG arity = project_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *proj_elem_dxlnode = (*project_list_dxlnode)[ul]; |
| CDXLScalarProjElem *sc_proj_elem_dxlop = |
| CDXLScalarProjElem::Cast(proj_elem_dxlnode->GetOperator()); |
| |
| const TargetEntry *te_child = |
| child_context->GetTargetEntry(sc_proj_elem_dxlop->Id()); |
| if (nullptr == te_child) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| sc_proj_elem_dxlop->Id()); |
| } |
| |
| // get type oid for project element's expression |
| GPOS_ASSERT(1 == proj_elem_dxlnode->Arity()); |
| |
| // find column type |
| OID oid_type = gpdb::ExprType((Node *) te_child->expr); |
| INT type_modifier = gpdb::ExprTypeMod((Node *) te_child->expr); |
| |
| // find the original varno and attno for this column |
| Index idx_varnoold = 0; |
| AttrNumber attno_old = 0; |
| |
| if (IsA(te_child->expr, Var)) |
| { |
| Var *pv = (Var *) te_child->expr; |
| idx_varnoold = pv->varnosyn; |
| attno_old = pv->varattnosyn; |
| } |
| else |
| { |
| idx_varnoold = OUTER_VAR; |
| attno_old = te_child->resno; |
| } |
| |
| // create a Var expression for this target list entry expression |
| Var *var = |
| gpdb::MakeVar(OUTER_VAR, te_child->resno, oid_type, type_modifier, |
| 0 // varlevelsup |
| ); |
| |
| // set old varno and varattno since makeVar does not set them |
| var->varnosyn = idx_varnoold; |
| var->varattnosyn = attno_old; |
| |
| CHAR *resname = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| sc_proj_elem_dxlop->GetMdNameAlias()->GetMDName()->GetBuffer()); |
| |
| TargetEntry *target_entry = |
| gpdb::MakeTargetEntry((Expr *) var, (AttrNumber)(ul + 1), resname, |
| false // resjunk |
| ); |
| |
| target_list = gpdb::LAppend(target_list, target_entry); |
| output_context->InsertMapping(sc_proj_elem_dxlop->Id(), target_entry); |
| } |
| |
| return target_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLFilterToQual |
| // |
| // @doc: |
| // Translates a DXL filter node into a Qual list. |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLFilterToQual( |
| const CDXLNode *filter_dxlnode, |
| const CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *child_contexts, |
| CDXLTranslateContext *output_context) |
| { |
| const ULONG arity = filter_dxlnode->Arity(); |
| if (0 == arity) |
| { |
| return NIL; |
| } |
| |
| GPOS_ASSERT(1 == arity); |
| |
| CDXLNode *filter_cond_dxlnode = (*filter_dxlnode)[0]; |
| GPOS_ASSERT(CTranslatorDXLToScalar::HasBoolResult(filter_cond_dxlnode, |
| m_md_accessor)); |
| |
| return TranslateDXLScCondToQual(filter_cond_dxlnode, base_table_context, |
| child_contexts, output_context); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLScCondToQual |
| // |
| // @doc: |
| // Translates a DXL scalar condition node node into a Qual list. |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLScCondToQual( |
| const CDXLNode *condition_dxlnode, |
| const CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *child_contexts, |
| CDXLTranslateContext *output_context) |
| { |
| List *quals_list = NIL; |
| |
| GPOS_ASSERT(CTranslatorDXLToScalar::HasBoolResult( |
| const_cast<CDXLNode *>(condition_dxlnode), m_md_accessor)); |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, base_table_context, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| Expr *expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| condition_dxlnode, &colid_var_mapping); |
| |
| quals_list = gpdb::LAppend(quals_list, expr); |
| |
| return quals_list; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslatePlanCosts |
| // |
| // @doc: |
| // Translates DXL plan costs into the GPDB cost variables |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslatePlanCosts(const CDXLNode *dxlnode, Plan *plan) |
| { |
| CDXLOperatorCost *costs = |
| CDXLPhysicalProperties::PdxlpropConvert(dxlnode->GetProperties()) |
| ->GetDXLOperatorCost(); |
| |
| plan->startup_cost = CostFromStr(costs->GetStartUpCostStr()); |
| plan->total_cost = CostFromStr(costs->GetTotalCostStr()); |
| plan->plan_width = CTranslatorUtils::GetIntFromStr(costs->GetWidthStr()); |
| |
| // In the Postgres planner, the estimates on each node are per QE |
| // process, whereas the row estimates in GPORCA are global, across all |
| // processes. Divide the row count estimate by the number of segments |
| // executing it. |
| plan->plan_rows = |
| ceil(CostFromStr(costs->GetRowsOutStr()) / |
| m_dxl_to_plstmt_context->GetCurrentSlice()->numsegments); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateProjListAndFilter |
| // |
| // @doc: |
| // Translates DXL proj list and filter into GPDB's target and qual lists, |
| // respectively |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslateProjListAndFilter( |
| const CDXLNode *project_list_dxlnode, const CDXLNode *filter_dxlnode, |
| const CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *child_contexts, List **targetlist_out, |
| List **qual_out, CDXLTranslateContext *output_context) |
| { |
| // translate proj list |
| *targetlist_out = TranslateDXLProjList( |
| project_list_dxlnode, |
| base_table_context, // base table translation context |
| child_contexts, output_context); |
| |
| // translate filter |
| *qual_out = TranslateDXLFilterToQual( |
| filter_dxlnode, |
| base_table_context, // base table translation context |
| child_contexts, output_context); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::AddSecurityQuals |
| // |
| // @doc: |
| // This method is used to fetch the range table entry from the rewritten |
| // parse tree based on the relId and add it's security quals in the quals |
| // list. It also modifies the varno of the VAR node present in the |
| // security quals and assigns it the value of the index i.e. the |
| // position of this rte at m_dxl_to_plstmt_context->m_rtable_entries_list |
| // (shortened as "rte_list") |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::AddSecurityQuals(OID relId, List **qual, Index *index) |
| { |
| SContextSecurityQuals ctxt_security_quals(relId); |
| |
| // Find the RTE in the parse tree based on the relId and add the security |
| // quals of that RTE to the m_security_quals list present in |
| // ctxt_security_quals struct. |
| FetchSecurityQuals(m_dxl_to_plstmt_context->m_orig_query, |
| &ctxt_security_quals); |
| |
| // The varno of the columns related to a particular table is different in |
| // the rewritten parse tree and the planned statement tree. In planned |
| // statement the varno of the columns is based on the index of the RTE |
| // at m_dxl_to_plstmt_context->m_rtable_entries_list. Since we are adding |
| // the security quals from the rewritten parse tree to planned statement |
| // tree we need to modify the varno of all the VAR nodes present in the |
| // security quals and assign it equal to index of the RTE in the rte_list. |
| SetSecurityQualsVarnoWalker((Node *) ctxt_security_quals.m_security_quals, |
| index); |
| |
| // Adding the security quals from m_security_quals list to the qual list |
| *qual = gpdb::ListConcat(*qual, ctxt_security_quals.m_security_quals); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::FetchSecurityQuals |
| // |
| // @doc: |
| // This method is used to walk the entire rewritten parse tree and |
| // search for a range table entry whose relid is equal to the m_relId |
| // field of ctxt_security_quals struct. On finding the RTE this method |
| // will also add the security quals present in it to the |
| // m_security_quals list of ctxt_security_quals struct. |
| // |
| //--------------------------------------------------------------------------- |
| BOOL |
| CTranslatorDXLToPlStmt::FetchSecurityQuals( |
| Query *parsetree, SContextSecurityQuals *ctxt_security_quals) |
| { |
| ListCell *lc; |
| |
| // Iterate through all the range table entries present in the the rtable |
| // of the parsetree and search for a range table entry whose relid is |
| // equal to ctxt_security_quals->m_relId. If found then add the security |
| // quals of that RTE in the ctxt_security_quals->m_security_quals list. |
| // If the range table entry contains a subquery then recurse through that |
| // subquery and continue the search. |
| foreach (lc, parsetree->rtable) |
| { |
| RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); |
| if (RTE_RELATION == rte->rtekind && |
| rte->relid == ctxt_security_quals->m_relId) |
| { |
| ctxt_security_quals->m_security_quals = gpdb::ListConcat( |
| ctxt_security_quals->m_security_quals, rte->securityQuals); |
| return true; |
| } |
| |
| if ((RTE_SUBQUERY == rte->rtekind || |
| RTE_TABLEFUNCTION == rte->rtekind) && |
| FetchSecurityQuals(rte->subquery, ctxt_security_quals)) |
| { |
| return true; |
| } |
| } |
| |
| // Recurse into ctelist |
| foreach (lc, parsetree->cteList) |
| { |
| CommonTableExpr *cte = lfirst_node(CommonTableExpr, lc); |
| |
| if (FetchSecurityQuals(castNode(Query, cte->ctequery), |
| ctxt_security_quals)) |
| { |
| return true; |
| } |
| } |
| |
| // Recurse into sublink subqueries. We have already recursed the sublink |
| // subqueries present in the rtable and ctelist. QTW_IGNORE_RC_SUBQUERIES |
| // flag indicates to avoid recursing subqueries present in rtable and |
| // ctelist |
| if (parsetree->hasSubLinks) |
| { |
| return gpdb::WalkQueryTree( |
| parsetree, |
| (bool (*)(Node *, void *)) CTranslatorDXLToPlStmt::FetchSecurityQualsWalker, |
| ctxt_security_quals, QTW_IGNORE_RC_SUBQUERIES); |
| } |
| |
| return false; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::FetchSecurityQualsWalker |
| // |
| // @doc: |
| // This method is a walker to recurse into SUBLINK nodes and search for |
| // an RTE having relid equal to m_relId field of ctxt_security_quals struct |
| // |
| //--------------------------------------------------------------------------- |
| BOOL |
| CTranslatorDXLToPlStmt::FetchSecurityQualsWalker( |
| Node *node, SContextSecurityQuals *ctxt_security_quals) |
| { |
| if (nullptr == node) |
| { |
| return false; |
| } |
| |
| // If the node is a SUBLINK, fetch its subselect node and start the |
| // search again for the RTE based on the m_relId field of |
| // ctxt_security_quals struct. If we found the RTE then the flag |
| // m_found_rte would have been set to true. In that case returning true |
| // which indicates to abort the walk immediately. |
| if (IsA(node, SubLink)) |
| { |
| SubLink *sub = (SubLink *) node; |
| |
| if (FetchSecurityQuals(castNode(Query, sub->subselect), |
| ctxt_security_quals)) |
| { |
| return true; |
| } |
| } |
| |
| return gpdb::WalkExpressionTree( |
| node, (bool (*)(Node *, void *)) CTranslatorDXLToPlStmt::FetchSecurityQualsWalker, |
| ctxt_security_quals); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::SetSecurityQualsVarnoWalker |
| // |
| // @doc: |
| // The varno of the columns related to a particular table is different in |
| // the rewritten parse tree and the planned statement tree. In planned |
| // statement the varno of the columns is based on the index of the RTE at |
| // m_dxl_to_plstmt_context->m_rtable_entries_list. Since we are adding |
| // the security quals from the rewritten parse tree to planned statement |
| // tree we need to modify the varno of all the VAR nodes present in the |
| // security quals and assign it equal to index of the RTE in the rte_list. |
| // |
| //--------------------------------------------------------------------------- |
| BOOL |
| CTranslatorDXLToPlStmt::SetSecurityQualsVarnoWalker(Node *node, Index *index) |
| { |
| if (nullptr == node) |
| { |
| return false; |
| } |
| |
| if (IsA(node, Var)) |
| { |
| ((Var *) node)->varno = *index; |
| return false; |
| } |
| |
| return gpdb::WalkExpressionTree( |
| node, (bool (*)(Node *, void *)) CTranslatorDXLToPlStmt::SetSecurityQualsVarnoWalker, |
| index); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateHashExprList |
| // |
| // @doc: |
| // Translates DXL hash expression list in a redistribute motion node into |
| // GPDB's hash expression and expression types lists, respectively |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslateHashExprList( |
| const CDXLNode *hash_expr_list_dxlnode, |
| const CDXLTranslateContext *child_context, List **hash_expr_out_list, |
| List **hash_expr_opfamilies_out_list, CDXLTranslateContext *output_context) |
| { |
| GPOS_ASSERT(NIL == *hash_expr_out_list); |
| GPOS_ASSERT(NIL == *hash_expr_opfamilies_out_list); |
| |
| List *hash_expr_list = NIL; |
| |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(child_context); |
| |
| const ULONG arity = hash_expr_list_dxlnode->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *hash_expr_dxlnode = (*hash_expr_list_dxlnode)[ul]; |
| |
| GPOS_ASSERT(1 == hash_expr_dxlnode->Arity()); |
| CDXLNode *expr_dxlnode = (*hash_expr_dxlnode)[0]; |
| |
| CMappingColIdVarPlStmt colid_var_mapping = |
| CMappingColIdVarPlStmt(m_mp, nullptr, child_contexts, |
| output_context, m_dxl_to_plstmt_context); |
| |
| Expr *expr = m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| expr_dxlnode, &colid_var_mapping); |
| |
| hash_expr_list = gpdb::LAppend(hash_expr_list, expr); |
| |
| GPOS_ASSERT((ULONG) gpdb::ListLength(hash_expr_list) == ul + 1); |
| } |
| |
| List *hash_expr_opfamilies = NIL; |
| if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution)) |
| { |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *hash_expr_dxlnode = (*hash_expr_list_dxlnode)[ul]; |
| CDXLScalarHashExpr *hash_expr_dxlop = |
| CDXLScalarHashExpr::Cast(hash_expr_dxlnode->GetOperator()); |
| const IMDId *opfamily = hash_expr_dxlop->MdidOpfamily(); |
| hash_expr_opfamilies = gpdb::LAppendOid( |
| hash_expr_opfamilies, CMDIdGPDB::CastMdid(opfamily)->Oid()); |
| } |
| } |
| |
| *hash_expr_out_list = hash_expr_list; |
| *hash_expr_opfamilies_out_list = hash_expr_opfamilies; |
| |
| // cleanup |
| child_contexts->Release(); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateSortCols |
| // |
| // @doc: |
| // Translates DXL sorting columns list into GPDB's arrays of sorting attribute numbers, |
| // and sorting operator ids, respectively. |
| // The two arrays must be allocated by the caller. |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::TranslateSortCols( |
| const CDXLNode *sort_col_list_dxl, |
| const CDXLTranslateContext *child_context, AttrNumber *att_no_sort_colids, |
| Oid *sort_op_oids, Oid *sort_collations_oids, bool *is_nulls_first) |
| { |
| const ULONG arity = sort_col_list_dxl->Arity(); |
| for (ULONG ul = 0; ul < arity; ul++) |
| { |
| CDXLNode *sort_col_dxlnode = (*sort_col_list_dxl)[ul]; |
| CDXLScalarSortCol *sc_sort_col_dxlop = |
| CDXLScalarSortCol::Cast(sort_col_dxlnode->GetOperator()); |
| |
| ULONG sort_colid = sc_sort_col_dxlop->GetColId(); |
| const TargetEntry *te_sort_col = |
| child_context->GetTargetEntry(sort_colid); |
| if (nullptr == te_sort_col) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| sort_colid); |
| } |
| |
| att_no_sort_colids[ul] = te_sort_col->resno; |
| sort_op_oids[ul] = |
| CMDIdGPDB::CastMdid(sc_sort_col_dxlop->GetMdIdSortOp())->Oid(); |
| if (sort_collations_oids) |
| { |
| sort_collations_oids[ul] = |
| gpdb::ExprCollation((Node *) te_sort_col->expr); |
| } |
| is_nulls_first[ul] = sc_sort_col_dxlop->IsSortedNullsFirst(); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::CostFromStr |
| // |
| // @doc: |
| // Parses a cost value from a string |
| // |
| //--------------------------------------------------------------------------- |
| Cost |
| CTranslatorDXLToPlStmt::CostFromStr(const CWStringBase *str) |
| { |
| CHAR *sz = CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| str->GetBuffer()); |
| return gpos::clib::Strtod(sz); |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::IsTgtTblDistributed |
| // |
| // @doc: |
| // Check if given operator is a DML on a distributed table |
| // |
| //--------------------------------------------------------------------------- |
| BOOL |
| CTranslatorDXLToPlStmt::IsTgtTblDistributed(CDXLOperator *dxlop) |
| { |
| if (EdxlopPhysicalDML != dxlop->GetDXLOperator()) |
| { |
| return false; |
| } |
| |
| CDXLPhysicalDML *phy_dml_dxlop = CDXLPhysicalDML::Cast(dxlop); |
| IMDId *mdid = phy_dml_dxlop->GetDXLTableDescr()->MDId(); |
| |
| return IMDRelation::EreldistrMasterOnly != |
| m_md_accessor->RetrieveRel(mdid)->GetRelDistribution(); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::AddJunkTargetEntryForColId |
| // |
| // @doc: |
| // Add a new target entry for the given colid to the given target list |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::AddJunkTargetEntryForColId( |
| List **target_list, CDXLTranslateContext *dxl_translate_ctxt, ULONG colid, |
| const char *resname) |
| { |
| GPOS_ASSERT(nullptr != target_list); |
| |
| const TargetEntry *target_entry = dxl_translate_ctxt->GetTargetEntry(colid); |
| |
| if (nullptr == target_entry) |
| { |
| // colid not found in translate context |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiDXL2PlStmtAttributeNotFound, |
| colid); |
| } |
| |
| // TODO: Oct 29, 2012; see if entry already exists in the target list |
| |
| OID expr_oid = gpdb::ExprType((Node *) target_entry->expr); |
| INT type_modifier = gpdb::ExprTypeMod((Node *) target_entry->expr); |
| Var *var = |
| gpdb::MakeVar(OUTER_VAR, target_entry->resno, expr_oid, type_modifier, |
| 0 // varlevelsup |
| ); |
| ULONG resno = gpdb::ListLength(*target_list) + 1; |
| CHAR *resname_str = PStrDup(resname); |
| TargetEntry *te_new = gpdb::MakeTargetEntry( |
| (Expr *) var, resno, resname_str, true /* resjunk */); |
| *target_list = gpdb::LAppend(*target_list, te_new); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::GetGPDBJoinTypeFromDXLJoinType |
| // |
| // @doc: |
| // Translates the join type from its DXL representation into the GPDB one |
| // |
| //--------------------------------------------------------------------------- |
| JoinType |
| CTranslatorDXLToPlStmt::GetGPDBJoinTypeFromDXLJoinType(EdxlJoinType join_type) |
| { |
| GPOS_ASSERT(EdxljtSentinel > join_type); |
| |
| JoinType jt = JOIN_INNER; |
| |
| switch (join_type) |
| { |
| case EdxljtInner: |
| jt = JOIN_INNER; |
| break; |
| case EdxljtLeft: |
| jt = JOIN_LEFT; |
| break; |
| case EdxljtFull: |
| jt = JOIN_FULL; |
| break; |
| case EdxljtRight: |
| jt = JOIN_RIGHT; |
| break; |
| case EdxljtIn: |
| jt = JOIN_SEMI; |
| break; |
| case EdxljtLeftAntiSemijoin: |
| jt = JOIN_ANTI; |
| break; |
| case EdxljtLeftAntiSemijoinNotIn: |
| jt = JOIN_LASJ_NOTIN; |
| break; |
| default: |
| GPOS_ASSERT(!"Unrecognized join type"); |
| } |
| |
| return jt; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLCtas |
| // |
| // @doc: |
| // Sets the vartypmod fields in the target entries of the given target list |
| // |
| //--------------------------------------------------------------------------- |
| void |
| CTranslatorDXLToPlStmt::SetVarTypMod(const CDXLPhysicalCTAS *phy_ctas_dxlop, |
| List *target_list) |
| { |
| // target list can be nullptr in CTAS |
| IntPtrArray *var_type_mod_array = phy_ctas_dxlop->GetVarTypeModArray(); |
| GPOS_ASSERT(var_type_mod_array->Size() == gpdb::ListLength(target_list)); |
| |
| ULONG ul = 0; |
| ListCell *lc = nullptr; |
| ForEach(lc, target_list) |
| { |
| TargetEntry *target_entry = (TargetEntry *) lfirst(lc); |
| GPOS_ASSERT(IsA(target_entry, TargetEntry)); |
| |
| if (IsA(target_entry->expr, Var)) |
| { |
| Var *var = (Var *) target_entry->expr; |
| var->vartypmod = *(*var_type_mod_array)[ul]; |
| } |
| ++ul; |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLCtas |
| // |
| // @doc: |
| // Translates a DXL CTAS node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLCtas( |
| const CDXLNode *ctas_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| CDXLPhysicalCTAS *phy_ctas_dxlop = |
| CDXLPhysicalCTAS::Cast(ctas_dxlnode->GetOperator()); |
| CDXLNode *project_list_dxlnode = (*ctas_dxlnode)[0]; |
| CDXLNode *child_dxlnode = (*ctas_dxlnode)[1]; |
| |
| GPOS_ASSERT( |
| nullptr == |
| phy_ctas_dxlop->GetDxlCtasStorageOption()->GetDXLCtasOptionArray()); |
| |
| CDXLTranslateContext child_context(m_mp, false, |
| output_context->GetColIdToParamIdMap()); |
| |
| Plan *plan = TranslateDXLOperatorToPlan(child_dxlnode, &child_context, |
| ctxt_translation_prev_siblings); |
| |
| // fix target list to match the required column names |
| CDXLTranslationContextArray *child_contexts = |
| GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp); |
| child_contexts->Append(&child_context); |
| |
| List *target_list = TranslateDXLProjList(project_list_dxlnode, |
| nullptr, // base_table_context |
| child_contexts, output_context); |
| SetVarTypMod(phy_ctas_dxlop, target_list); |
| |
| SetParamIds(plan); |
| |
| // cleanup |
| child_contexts->Release(); |
| |
| // translate operator costs |
| TranslatePlanCosts(ctas_dxlnode, plan); |
| |
| GpPolicy *distr_policy = |
| TranslateDXLPhyCtasToDistrPolicy(phy_ctas_dxlop, target_list); |
| m_dxl_to_plstmt_context->AddCtasInfo(distr_policy); |
| |
| GPOS_ASSERT(IMDRelation::EreldistrMasterOnly != |
| phy_ctas_dxlop->Ereldistrpolicy()); |
| |
| m_is_tgt_tbl_distributed = true; |
| |
| // Add a result node on top with the correct projection list |
| Result *result = MakeNode(Result); |
| Plan *result_plan = &(result->plan); |
| result_plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| result_plan->lefttree = plan; |
| |
| result_plan->targetlist = target_list; |
| SetParamIds(result_plan); |
| |
| plan = (Plan *) result; |
| |
| return (Plan *) plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLPhyCtasToDistrPolicy |
| // |
| // @doc: |
| // Translates distribution policy given by a physical CTAS operator |
| // |
| //--------------------------------------------------------------------------- |
| GpPolicy * |
| CTranslatorDXLToPlStmt::TranslateDXLPhyCtasToDistrPolicy( |
| const CDXLPhysicalCTAS *dxlop, List * /*target_list*/) |
| { |
| ULongPtrArray *distr_col_pos_array = dxlop->GetDistrColPosArray(); |
| |
| const ULONG num_of_distr_cols = |
| (distr_col_pos_array == nullptr) ? 0 : distr_col_pos_array->Size(); |
| |
| ULONG num_of_distr_cols_alloc = 1; |
| if (0 < num_of_distr_cols) |
| { |
| num_of_distr_cols_alloc = num_of_distr_cols; |
| } |
| |
| // always set numsegments to ALL for CTAS |
| GpPolicy *distr_policy = |
| gpdb::MakeGpPolicy(POLICYTYPE_PARTITIONED, num_of_distr_cols_alloc, |
| gpdb::GetGPSegmentCount()); |
| |
| GPOS_ASSERT(IMDRelation::EreldistrHash == dxlop->Ereldistrpolicy() || |
| IMDRelation::EreldistrRandom == dxlop->Ereldistrpolicy() || |
| IMDRelation::EreldistrReplicated == dxlop->Ereldistrpolicy()); |
| |
| if (IMDRelation::EreldistrReplicated == dxlop->Ereldistrpolicy()) |
| { |
| distr_policy->ptype = POLICYTYPE_REPLICATED; |
| } |
| else |
| { |
| distr_policy->ptype = POLICYTYPE_PARTITIONED; |
| } |
| |
| distr_policy->nattrs = 0; |
| if (IMDRelation::EreldistrHash == dxlop->Ereldistrpolicy()) |
| { |
| GPOS_ASSERT(0 < num_of_distr_cols); |
| distr_policy->nattrs = num_of_distr_cols; |
| IMdIdArray *opclasses = dxlop->GetDistrOpclasses(); |
| GPOS_ASSERT(opclasses->Size() == num_of_distr_cols); |
| for (ULONG ul = 0; ul < num_of_distr_cols; ul++) |
| { |
| ULONG col_pos_idx = *((*distr_col_pos_array)[ul]); |
| distr_policy->attrs[ul] = col_pos_idx + 1; |
| |
| Oid opclass = CMDIdGPDB::CastMdid((*opclasses)[ul])->Oid(); |
| distr_policy->opclasses[ul] = opclass; |
| } |
| } |
| return distr_policy; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLCtasStorageOptions |
| // |
| // @doc: |
| // Translates CTAS options |
| // |
| //--------------------------------------------------------------------------- |
| List * |
| CTranslatorDXLToPlStmt::TranslateDXLCtasStorageOptions( |
| CDXLCtasStorageOptions::CDXLCtasOptionArray *ctas_storage_options) |
| { |
| if (nullptr == ctas_storage_options) |
| { |
| return NIL; |
| } |
| |
| const ULONG num_of_options = ctas_storage_options->Size(); |
| List *options = NIL; |
| for (ULONG ul = 0; ul < num_of_options; ul++) |
| { |
| CDXLCtasStorageOptions::CDXLCtasOption *pdxlopt = |
| (*ctas_storage_options)[ul]; |
| CWStringBase *str_name = pdxlopt->m_str_name; |
| CWStringBase *str_value = pdxlopt->m_str_value; |
| DefElem *def_elem = MakeNode(DefElem); |
| def_elem->defname = |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| str_name->GetBuffer()); |
| |
| if (!pdxlopt->m_is_null) |
| { |
| NodeTag arg_type = (NodeTag) pdxlopt->m_type; |
| |
| GPOS_ASSERT(T_Integer == arg_type || T_String == arg_type); |
| if (T_Integer == arg_type) |
| { |
| def_elem->arg = (Node *) gpdb::MakeIntegerValue( |
| CTranslatorUtils::GetLongFromStr(str_value)); |
| } |
| else |
| { |
| def_elem->arg = (Node *) gpdb::MakeStringValue( |
| CTranslatorUtils::CreateMultiByteCharStringFromWCString( |
| str_value->GetBuffer())); |
| } |
| } |
| |
| options = gpdb::LAppend(options, def_elem); |
| } |
| |
| return options; |
| } |
| |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLBitmapTblScan |
| // |
| // @doc: |
| // Translates a DXL bitmap table scan node into a BitmapHeapScan node |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLBitmapTblScan( |
| const CDXLNode *bitmapscan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings) |
| { |
| BOOL is_dynamic = false; |
| const CDXLTableDescr *table_descr = nullptr; |
| |
| CDXLOperator *dxl_operator = bitmapscan_dxlnode->GetOperator(); |
| if (EdxlopPhysicalBitmapTableScan == dxl_operator->GetDXLOperator()) |
| { |
| table_descr = |
| CDXLPhysicalBitmapTableScan::Cast(dxl_operator)->GetDXLTableDescr(); |
| } |
| else |
| { |
| GPOS_ASSERT(EdxlopPhysicalDynamicBitmapTableScan == |
| dxl_operator->GetDXLOperator()); |
| CDXLPhysicalDynamicBitmapTableScan *phy_dyn_bitmap_tblscan_dxlop = |
| CDXLPhysicalDynamicBitmapTableScan::Cast(dxl_operator); |
| table_descr = phy_dyn_bitmap_tblscan_dxlop->GetDXLTableDescr(); |
| |
| is_dynamic = true; |
| } |
| |
| // translation context for column mappings in the base relation |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| const IMDRelation *md_rel = m_md_accessor->RetrieveRel(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()); |
| GPOS_ASSERT(table_descr->LockMode() != -1); |
| gpdb::GPDBLockRelationOid(mdid->Oid(), table_descr->LockMode()); |
| |
| Index index = ProcessDXLTblDescr(table_descr, &base_table_context); |
| |
| DynamicBitmapHeapScan *dscan; |
| BitmapHeapScan *bitmap_tbl_scan; |
| |
| dscan = MakeNode(DynamicBitmapHeapScan); |
| if (is_dynamic) |
| { |
| bitmap_tbl_scan = &dscan->bitmapheapscan; |
| |
| CDXLPhysicalDynamicBitmapTableScan *phy_dyn_bitmap_tblscan_dxlop = |
| CDXLPhysicalDynamicBitmapTableScan::Cast(dxl_operator); |
| |
| IMdIdArray *parts = phy_dyn_bitmap_tblscan_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); |
| } |
| |
| dscan->partOids = oids_list; |
| |
| OID oid_type = |
| CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) |
| ->Oid(); |
| |
| dscan->join_prune_paramids = TranslateJoinPruneParamids( |
| phy_dyn_bitmap_tblscan_dxlop->GetSelectorIds(), oid_type, |
| m_dxl_to_plstmt_context); |
| } |
| else |
| { |
| bitmap_tbl_scan = MakeNode(BitmapHeapScan); |
| } |
| bitmap_tbl_scan->scan.scanrelid = index; |
| |
| Plan *plan = &(bitmap_tbl_scan->scan.plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(bitmapscan_dxlnode, plan); |
| |
| GPOS_ASSERT(4 == bitmapscan_dxlnode->Arity()); |
| |
| // translate proj list and filter |
| CDXLNode *project_list_dxlnode = (*bitmapscan_dxlnode)[0]; |
| CDXLNode *filter_dxlnode = (*bitmapscan_dxlnode)[1]; |
| CDXLNode *recheck_cond_dxlnode = (*bitmapscan_dxlnode)[2]; |
| CDXLNode *bitmap_access_path_dxlnode = (*bitmapscan_dxlnode)[3]; |
| |
| List *quals_list = nullptr; |
| TranslateProjListAndFilter( |
| project_list_dxlnode, filter_dxlnode, |
| &base_table_context, // translate context for the base table |
| ctxt_translation_prev_siblings, &plan->targetlist, &quals_list, |
| output_context); |
| plan->qual = quals_list; |
| |
| bitmap_tbl_scan->bitmapqualorig = TranslateDXLFilterToQual( |
| recheck_cond_dxlnode, &base_table_context, |
| ctxt_translation_prev_siblings, output_context); |
| |
| bitmap_tbl_scan->scan.plan.lefttree = TranslateDXLBitmapAccessPath( |
| bitmap_access_path_dxlnode, output_context, md_rel, table_descr, |
| &base_table_context, ctxt_translation_prev_siblings, bitmap_tbl_scan); |
| SetParamIds(plan); |
| |
| if (is_dynamic) |
| { |
| return (Plan *) dscan; |
| } |
| return (Plan *) bitmap_tbl_scan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLBitmapAccessPath |
| // |
| // @doc: |
| // Translate the tree of bitmap index operators that are under the given |
| // (dynamic) bitmap table scan. |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLBitmapAccessPath( |
| const CDXLNode *bitmap_access_path_dxlnode, |
| CDXLTranslateContext *output_context, const IMDRelation *md_rel, |
| const CDXLTableDescr *table_descr, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings, |
| BitmapHeapScan *bitmap_tbl_scan) |
| { |
| Edxlopid dxl_op_id = |
| bitmap_access_path_dxlnode->GetOperator()->GetDXLOperator(); |
| if (EdxlopScalarBitmapIndexProbe == dxl_op_id) |
| { |
| return TranslateDXLBitmapIndexProbe( |
| bitmap_access_path_dxlnode, output_context, md_rel, table_descr, |
| base_table_context, ctxt_translation_prev_siblings, |
| bitmap_tbl_scan); |
| } |
| GPOS_ASSERT(EdxlopScalarBitmapBoolOp == dxl_op_id); |
| |
| return TranslateDXLBitmapBoolOp( |
| bitmap_access_path_dxlnode, output_context, md_rel, table_descr, |
| base_table_context, ctxt_translation_prev_siblings, bitmap_tbl_scan); |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToScalar::TranslateDXLBitmapBoolOp |
| // |
| // @doc: |
| // Translates a DML bitmap bool op expression |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLBitmapBoolOp( |
| const CDXLNode *bitmap_boolop_dxlnode, CDXLTranslateContext *output_context, |
| const IMDRelation *md_rel, const CDXLTableDescr *table_descr, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings, |
| BitmapHeapScan *bitmap_tbl_scan) |
| { |
| GPOS_ASSERT(nullptr != bitmap_boolop_dxlnode); |
| GPOS_ASSERT(EdxlopScalarBitmapBoolOp == |
| bitmap_boolop_dxlnode->GetOperator()->GetDXLOperator()); |
| |
| CDXLScalarBitmapBoolOp *sc_bitmap_boolop_dxlop = |
| CDXLScalarBitmapBoolOp::Cast(bitmap_boolop_dxlnode->GetOperator()); |
| |
| CDXLNode *left_tree_dxlnode = (*bitmap_boolop_dxlnode)[0]; |
| CDXLNode *right_tree_dxlnode = (*bitmap_boolop_dxlnode)[1]; |
| |
| Plan *left_plan = TranslateDXLBitmapAccessPath( |
| left_tree_dxlnode, output_context, md_rel, table_descr, |
| base_table_context, ctxt_translation_prev_siblings, bitmap_tbl_scan); |
| Plan *right_plan = TranslateDXLBitmapAccessPath( |
| right_tree_dxlnode, output_context, md_rel, table_descr, |
| base_table_context, ctxt_translation_prev_siblings, bitmap_tbl_scan); |
| List *child_plan_list = ListMake2(left_plan, right_plan); |
| |
| Plan *plan = nullptr; |
| |
| if (CDXLScalarBitmapBoolOp::EdxlbitmapAnd == |
| sc_bitmap_boolop_dxlop->GetDXLBitmapOpType()) |
| { |
| BitmapAnd *bitmapand = MakeNode(BitmapAnd); |
| bitmapand->plan.plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| bitmapand->bitmapplans = child_plan_list; |
| bitmapand->plan.targetlist = nullptr; |
| bitmapand->plan.qual = nullptr; |
| plan = (Plan *) bitmapand; |
| } |
| else |
| { |
| BitmapOr *bitmapor = MakeNode(BitmapOr); |
| bitmapor->plan.plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| bitmapor->bitmapplans = child_plan_list; |
| bitmapor->plan.targetlist = nullptr; |
| bitmapor->plan.qual = nullptr; |
| plan = (Plan *) bitmapor; |
| } |
| |
| |
| return plan; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::TranslateDXLBitmapIndexProbe |
| // |
| // @doc: |
| // Translate CDXLScalarBitmapIndexProbe into a BitmapIndexScan |
| // or a DynamicBitmapIndexScan |
| // |
| //--------------------------------------------------------------------------- |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLBitmapIndexProbe( |
| const CDXLNode *bitmap_index_probe_dxlnode, |
| CDXLTranslateContext *output_context, const IMDRelation *md_rel, |
| const CDXLTableDescr *table_descr, |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings, |
| BitmapHeapScan *bitmap_tbl_scan) |
| { |
| CDXLScalarBitmapIndexProbe *sc_bitmap_idx_probe_dxlop = |
| CDXLScalarBitmapIndexProbe::Cast( |
| bitmap_index_probe_dxlnode->GetOperator()); |
| |
| BitmapIndexScan *bitmap_idx_scan; |
| DynamicBitmapIndexScan *dyn_bitmap_idx_scan; |
| |
| if (IsA(bitmap_tbl_scan, DynamicBitmapHeapScan)) |
| { |
| /* It's a Dynamic Bitmap Index Scan */ |
| dyn_bitmap_idx_scan = MakeNode(DynamicBitmapIndexScan); |
| bitmap_idx_scan = &(dyn_bitmap_idx_scan->biscan); |
| } |
| else |
| { |
| dyn_bitmap_idx_scan = nullptr; |
| bitmap_idx_scan = MakeNode(BitmapIndexScan); |
| } |
| bitmap_idx_scan->scan.scanrelid = bitmap_tbl_scan->scan.scanrelid; |
| |
| CMDIdGPDB *mdid_index = CMDIdGPDB::CastMdid( |
| sc_bitmap_idx_probe_dxlop->GetDXLIndexDescr()->MDId()); |
| const IMDIndex *index = m_md_accessor->RetrieveIndex(mdid_index); |
| Oid index_oid = mdid_index->Oid(); |
| // Lock any index we are to scan, since it may not have been properly locked |
| // by the parser (e.g in case of generated scans for partitioned indexes) |
| gpdb::GPDBLockRelationOid(index_oid, table_descr->LockMode()); |
| |
| GPOS_ASSERT(InvalidOid != index_oid); |
| bitmap_idx_scan->indexid = index_oid; |
| Plan *plan = &(bitmap_idx_scan->scan.plan); |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| GPOS_ASSERT(1 == bitmap_index_probe_dxlnode->Arity()); |
| CDXLNode *index_cond_list_dxlnode = (*bitmap_index_probe_dxlnode)[0]; |
| List *index_cond = NIL; |
| List *index_orig_cond = NIL; |
| |
| TranslateIndexConditions( |
| index_cond_list_dxlnode, table_descr, true /*is_bitmap_index_probe*/, |
| index, md_rel, output_context, base_table_context, |
| ctxt_translation_prev_siblings, &index_cond, &index_orig_cond); |
| |
| bitmap_idx_scan->indexqual = index_cond; |
| bitmap_idx_scan->indexqualorig = index_orig_cond; |
| /* |
| * As of 8.4, the indexstrategy and indexsubtype fields are no longer |
| * available or needed in IndexScan. Ignore them. |
| */ |
| SetParamIds(plan); |
| |
| return plan; |
| } |
| |
| // translates a DXL Value Scan node into a GPDB Value scan node |
| Plan * |
| CTranslatorDXLToPlStmt::TranslateDXLValueScan( |
| const CDXLNode *value_scan_dxlnode, CDXLTranslateContext *output_context, |
| CDXLTranslationContextArray * /*ctxt_translation_prev_siblings*/) |
| { |
| // translation context for column mappings |
| CDXLTranslateContextBaseTable base_table_context(m_mp); |
| |
| // we will add the new range table entry as the last element of the range table |
| Index index = |
| gpdb::ListLength(m_dxl_to_plstmt_context->GetRTableEntriesList()) + 1; |
| |
| base_table_context.SetRelIndex(index); |
| |
| // create value scan node |
| ValuesScan *value_scan = MakeNode(ValuesScan); |
| value_scan->scan.scanrelid = index; |
| Plan *plan = &(value_scan->scan.plan); |
| |
| RangeTblEntry *rte = TranslateDXLValueScanToRangeTblEntry( |
| value_scan_dxlnode, output_context, &base_table_context); |
| GPOS_ASSERT(nullptr != rte); |
| |
| value_scan->values_lists = (List *) gpdb::CopyObject(rte->values_lists); |
| |
| m_dxl_to_plstmt_context->AddRTE(rte); |
| |
| plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId(); |
| |
| // translate operator costs |
| TranslatePlanCosts(value_scan_dxlnode, plan); |
| |
| // a table scan node must have at least 2 children: projection list and at least 1 value list |
| GPOS_ASSERT(2 <= value_scan_dxlnode->Arity()); |
| |
| CDXLNode *project_list_dxlnode = (*value_scan_dxlnode)[EdxltsIndexProjList]; |
| |
| // translate proj list |
| List *target_list = TranslateDXLProjList( |
| project_list_dxlnode, &base_table_context, nullptr, output_context); |
| |
| plan->targetlist = target_list; |
| |
| return (Plan *) value_scan; |
| } |
| |
| List * |
| CTranslatorDXLToPlStmt::TranslateNestLoopParamList( |
| CDXLColRefArray *pdrgdxlcrOuterRefs, CDXLTranslateContext *dxltrctxLeft, |
| CDXLTranslateContext *dxltrctxRight) |
| { |
| List *nest_params_list = NIL; |
| for (ULONG ul = 0; ul < pdrgdxlcrOuterRefs->Size(); ul++) |
| { |
| CDXLColRef *pdxlcr = (*pdrgdxlcrOuterRefs)[ul]; |
| ULONG ulColid = pdxlcr->Id(); |
| // left child context contains the target entry for the nest params col refs |
| const TargetEntry *target_entry = dxltrctxLeft->GetTargetEntry(ulColid); |
| GPOS_ASSERT(nullptr != target_entry); |
| Var *old_var = (Var *) target_entry->expr; |
| |
| Var *new_var = |
| gpdb::MakeVar(OUTER_VAR, target_entry->resno, old_var->vartype, |
| old_var->vartypmod, 0 /*varlevelsup*/); |
| new_var->varnosyn = old_var->varnosyn; |
| new_var->varattnosyn = old_var->varattnosyn; |
| |
| NestLoopParam *nest_params = MakeNode(NestLoopParam); |
| // right child context contains the param entry for the nest params col refs |
| const CMappingElementColIdParamId *colid_param_mapping = |
| dxltrctxRight->GetParamIdMappingElement(ulColid); |
| GPOS_ASSERT(nullptr != colid_param_mapping); |
| nest_params->paramno = colid_param_mapping->ParamId(); |
| nest_params->paramval = new_var; |
| nest_params_list = |
| gpdb::LAppend(nest_params_list, (void *) nest_params); |
| } |
| return nest_params_list; |
| } |
| |
| void |
| CTranslatorDXLToPlStmt::CheckSafeTargetListForAOTables(List *target_list) |
| { |
| ListCell *lc = nullptr; |
| |
| ForEach(lc, target_list) |
| { |
| TargetEntry *te = (TargetEntry *) lfirst(lc); |
| if (te->resorigcol < 0 && |
| te->resorigcol != SelfItemPointerAttributeNumber && |
| te->resorigcol != TableOidAttributeNumber && |
| te->resorigcol != GpSegmentIdAttributeNumber && |
| te->resorigcol != GpForeignServerAttributeNumber) |
| { |
| GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, |
| GPOS_WSZ_LIT("Invalid system target list found for AO table")); |
| } |
| } |
| } |
| |
| List * |
| CTranslatorDXLToPlStmt::CreateDirectCopyTargetList(List *target_list) |
| { |
| List *result_target_list = NIL; |
| ListCell *lc = nullptr; |
| |
| ForEach(lc, target_list) |
| { |
| TargetEntry *te = (TargetEntry *) lfirst(lc); |
| Node *expr = (Node *) te->expr; |
| |
| Var *var = gpdb::MakeVar(OUTER_VAR, te->resno, gpdb::ExprType(expr), |
| gpdb::ExprTypeMod(expr), 0 /* varlevelsup */); |
| TargetEntry *new_te = gpdb::MakeTargetEntry((Expr *) var, te->resno, te->resname, te->resjunk); |
| |
| result_target_list = gpdb::LAppend(result_target_list, new_te); |
| } |
| |
| return result_target_list; |
| } |
| |
| List * |
| CTranslatorDXLToPlStmt::GetRelationActiveColums(const IMDRelation *md_rel) |
| { |
| List *result_list = NIL; |
| ULONG resno = 1; |
| |
| const ULONG num_of_rel_cols = md_rel->ColumnCount(); |
| |
| for (ULONG ul = 0; ul < num_of_rel_cols; ul++) |
| { |
| const IMDColumn *md_col = md_rel->GetMdCol(ul); |
| |
| if (md_col->IsSystemColumn()) |
| { |
| continue; |
| } |
| |
| if (!md_col->IsDropped()) |
| { |
| result_list = gpdb::LAppendInt(result_list, resno); |
| } |
| |
| resno++; |
| } |
| |
| return result_list; |
| } |
| |
| // A bool Const expression is used as index condition if index column is used |
| // as part of ORDER BY clause. Because ORDER BY doesn't have any index conditions. |
| // This function checks if index is used for Order by. |
| bool |
| CTranslatorDXLToPlStmt::IsIndexForOrderBy( |
| CDXLTranslateContextBaseTable *base_table_context, |
| CDXLTranslationContextArray *ctxt_translation_prev_siblings, |
| CDXLTranslateContext *output_context, CDXLNode *index_cond_list_dxlnode) |
| { |
| const ULONG arity = index_cond_list_dxlnode->Arity(); |
| CMappingColIdVarPlStmt colid_var_mapping( |
| m_mp, base_table_context, ctxt_translation_prev_siblings, |
| output_context, m_dxl_to_plstmt_context); |
| if (arity == 1) |
| { |
| Expr *index_cond_expr = |
| m_translator_dxl_to_scalar->TranslateDXLToScalar( |
| (*index_cond_list_dxlnode)[0], &colid_var_mapping); |
| if (IsA(index_cond_expr, Const)) |
| { |
| return true; |
| } |
| return false; |
| } |
| return false; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // @function: |
| // CTranslatorDXLToPlStmt::ExtractParallelWorkersFromDXL |
| // |
| // @doc: |
| // Extract parallel workers count from DXL node tree recursively. |
| // Since parallel degree is uniform across all parallel scans in a query, |
| // returns the first parallel degree found from any CDXLPhysicalParallelTableScan, |
| // or 1 if no parallel scan exists. |
| // |
| //--------------------------------------------------------------------------- |
| ULONG |
| CTranslatorDXLToPlStmt::ExtractParallelWorkersFromDXL(const CDXLNode *dxlnode) |
| { |
| if (nullptr == dxlnode) |
| { |
| return 1; |
| } |
| |
| CDXLOperator *dxlop = dxlnode->GetOperator(); |
| if (EdxlopPhysicalParallelTableScan == dxlop->GetDXLOperator()) |
| { |
| // Return parallel workers from the parallel table scan operator |
| // All parallel scans in the query share the same parallel degree |
| CDXLPhysicalParallelTableScan *parallel_scan_dxlop = |
| CDXLPhysicalParallelTableScan::Cast(dxlop); |
| return parallel_scan_dxlop->UlParallelWorkers(); |
| } |
| else if (EdxlopPhysicalTableScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalDynamicTableScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalIndexScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalIndexOnlyScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalBitmapTableScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalDynamicBitmapTableScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalForeignScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalDynamicForeignScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalDynamicIndexScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalDynamicIndexOnlyScan == dxlop->GetDXLOperator() || |
| EdxlopPhysicalValuesScan == dxlop->GetDXLOperator()) |
| { |
| // Non-parallel scans (table, index, bitmap, foreign, values) |
| // These are leaf nodes in terms of parallel worker extraction |
| // Return 1 to indicate no parallel workers |
| return 1; |
| } |
| else if (EdxlopPhysicalMotionGather == dxlop->GetDXLOperator() || |
| EdxlopPhysicalMotionBroadcast == dxlop->GetDXLOperator() || |
| EdxlopPhysicalMotionRedistribute == dxlop->GetDXLOperator() || |
| EdxlopPhysicalMotionRandom == dxlop->GetDXLOperator() || |
| EdxlopPhysicalMotionRoutedDistribute == dxlop->GetDXLOperator()) |
| { |
| // Motion node creates a slice boundary - do not recurse into child |
| // The child's parallel workers belong to the sending slice, not receiving slice |
| // Return 0 to indicate the receiving slice (current slice) has no parallel workers |
| return 1; |
| } |
| |
| // Recursively check child nodes, return early when first parallel scan is found |
| for (ULONG ul = 0; ul < dxlnode->Arity(); ul++) |
| { |
| ULONG child_parallel_workers = ExtractParallelWorkersFromDXL((*dxlnode)[ul]); |
| if (child_parallel_workers > 1) |
| { |
| return child_parallel_workers; |
| } |
| } |
| |
| return 1; |
| } |
| |
| // EOF |