Esempio n. 1
0
//---------------------------------------------------------------------------
//	@function:
//		CLogical::PpartinfoDeriveCombine
//
//	@doc:
//		Common case of combining partition info of all logical children
//
//---------------------------------------------------------------------------
CPartInfo *
CLogical::PpartinfoDeriveCombine
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl
	)
{
	const ULONG ulArity = exprhdl.UlArity();
	GPOS_ASSERT(0 < ulArity);

	CPartInfo *ppartinfo = GPOS_NEW(pmp) CPartInfo(pmp);
	
	for (ULONG ul = 0; ul < ulArity; ul++)
	{
		CPartInfo *ppartinfoChild = NULL;
		if (exprhdl.FScalarChild(ul))
		{
			ppartinfoChild = exprhdl.Pdpscalar(ul)->Ppartinfo();
		}
		else
		{
			ppartinfoChild = exprhdl.Pdprel(ul)->Ppartinfo();
		}
		GPOS_ASSERT(NULL != ppartinfoChild);
		CPartInfo *ppartinfoCombined = CPartInfo::PpartinfoCombine(pmp, ppartinfo, ppartinfoChild);
		ppartinfo->Release();
		ppartinfo = ppartinfoCombined;
	}

	return ppartinfo;
}
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalPartitionSelector::PdsRequired
//
//	@doc:
//		Compute required distribution of the n-th child
//
//---------------------------------------------------------------------------
CDistributionSpec *
CPhysicalPartitionSelector::PdsRequired
	(
	IMemoryPool *mp,
	CExpressionHandle &exprhdl,
	CDistributionSpec *pdsInput,
	ULONG child_index,
	CDrvdProp2dArray *, // pdrgpdpCtxt
	ULONG // ulOptReq
	)
	const
{
	GPOS_ASSERT(0 == child_index);

	CDrvdPropRelational *pdprelDrvd = exprhdl.GetRelationalProperties();
	CPartInfo *ppartinfo = pdprelDrvd->Ppartinfo();
	BOOL fCovered = ppartinfo->FContainsScanId(m_scan_id);

	if (fCovered)
	{
		// if partition consumer is defined below, do not pass distribution
		// requirements down as this will cause the consumer and enforcer to be
		// in separate slices
		return GPOS_NEW(mp) CDistributionSpecAny(this->Eopid());
	}

	return PdsPassThru(mp, exprhdl, pdsInput, child_index);
}
Esempio n. 3
0
//---------------------------------------------------------------------------
//	@function:
//		CLogicalSelect::PexprPartPred
//
//	@doc:
//		Compute partition predicate to pass down to n-th child
//
//---------------------------------------------------------------------------
CExpression *
CLogicalSelect::PexprPartPred
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl,
	CExpression *, //pexprInput
	ULONG
#ifdef GPOS_DEBUG
	ulChildIndex
#endif //GPOS_DEBUG
	)
	const
{
	GPOS_ASSERT(0 == ulChildIndex);

	// in case of subquery in select predicate, we cannot extract the whole
	// predicate, and it would not be helpful anyway, so return NULL
	if (exprhdl.Pdpscalar(1 /*ulChildIndex*/)->FHasSubquery())
	{
		return NULL;
	}

	CExpression *pexprScalar = exprhdl.PexprScalarChild(1 /*ulChildIndex*/);
	GPOS_ASSERT(NULL != pexprScalar);

	// get partition keys
	CPartInfo *ppartinfo = exprhdl.Pdprel()->Ppartinfo();
	GPOS_ASSERT(NULL != ppartinfo);

	// we assume that the select is right on top of the dynamic get, so there
	// should be only one part consumer. If there is more, then we are higher up so
	// we do not push down any predicates
	if (1 != ppartinfo->UlConsumers())
	{
		return NULL;
	}

	CExpression *pexprPredOnPartKey = NULL;

	DrgPpartkeys *pdrgppartkeys = ppartinfo->Pdrgppartkeys(0 /*ulPos*/);
	const ULONG ulKeySets = pdrgppartkeys->UlLength();
	for (ULONG ul = 0; NULL == pexprPredOnPartKey && ul < ulKeySets; ul++)
	{
		pexprPredOnPartKey = CPredicateUtils::PexprExtractPredicatesOnPartKeys
												(
												pmp,
												pexprScalar,
												(*pdrgppartkeys)[ul]->Pdrgpdrgpcr(),
												NULL, //pcrsAllowedRefs
												true //fUseConstraints
												);
	}

	return pexprPredOnPartKey;
}
Esempio n. 4
0
//---------------------------------------------------------------------------
//	@function:
//		CLogical::PpartinfoPassThruOuter
//
//	@doc:
//		Common case of common case of passing through partition consumer array
//
//---------------------------------------------------------------------------
CPartInfo *
CLogical::PpartinfoPassThruOuter
	(
	CExpressionHandle &exprhdl
	)
{
	CPartInfo *ppartinfo = exprhdl.Pdprel(0 /*ulChildIndex*/)->Ppartinfo();
	GPOS_ASSERT(NULL != ppartinfo);
	ppartinfo->AddRef();
	return ppartinfo;
}
Esempio n. 5
0
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalMotion::PppsRequired
//
//	@doc:
//		Compute required partition propagation of the n-th child
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysicalMotion::PppsRequired
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	ULONG 
#ifdef GPOS_DEBUG
	ulChildIndex
#endif // GPOS_DEBUG
	,
	DrgPdp *, //pdrgpdpCtxt,
	ULONG //ulOptReq
	)
{
	GPOS_ASSERT(0 == ulChildIndex);
	GPOS_ASSERT(NULL != pppsRequired);
	
	CPartIndexMap *ppimReqd = pppsRequired->Ppim();
	CPartFilterMap *ppfmReqd = pppsRequired->Ppfm();
	
	DrgPul *pdrgpul = ppimReqd->PdrgpulScanIds(pmp);
	
	CPartIndexMap *ppimResult = GPOS_NEW(pmp) CPartIndexMap(pmp);
	CPartFilterMap *ppfmResult = GPOS_NEW(pmp) CPartFilterMap(pmp);
	
	/// get derived part consumers
	CPartInfo *ppartinfo = exprhdl.Pdprel(0)->Ppartinfo();
	
	const ULONG ulPartIndexSize = pdrgpul->UlLength();
	
	for (ULONG ul = 0; ul < ulPartIndexSize; ul++)
	{
		ULONG ulPartIndexId = *((*pdrgpul)[ul]);

		if (!ppartinfo->FContainsScanId(ulPartIndexId))
		{
			// part index id does not exist in child nodes: do not push it below 
			// the motion
			continue;
		}

		ppimResult->AddRequiredPartPropagation(ppimReqd, ulPartIndexId, CPartIndexMap::EppraPreservePropagators);
		(void) ppfmResult->FCopyPartFilter(m_pmp, ulPartIndexId, ppfmReqd);
	}
		
	pdrgpul->Release();

	return GPOS_NEW(pmp) CPartitionPropagationSpec(ppimResult, ppfmResult);
}
Esempio n. 6
0
//---------------------------------------------------------------------------
//	@function:
//		CPhysical::PppsRequiredPushThruUnresolvedUnary
//
//	@doc:
//		Helper function for pushing unresolved partition propagation in unary
//		operators
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysical::PppsRequiredPushThruUnresolvedUnary
	(
	IMemoryPool *mp,
	CExpressionHandle &exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	EPropogatePartConstraint eppcPropogate
	)
{
	GPOS_ASSERT(NULL != pppsRequired);

	CPartInfo *ppartinfo = exprhdl.GetRelationalProperties(0)->Ppartinfo();
		
	CPartIndexMap *ppimReqd = pppsRequired->Ppim();
	CPartFilterMap *ppfmReqd = pppsRequired->Ppfm();

	ULongPtrArray *pdrgpul = ppimReqd->PdrgpulScanIds(mp);
	
	CPartIndexMap *ppimResult = GPOS_NEW(mp) CPartIndexMap(mp);
	CPartFilterMap *ppfmResult = GPOS_NEW(mp) CPartFilterMap(mp);

	const ULONG ulPartIndexIds = pdrgpul->Size();
		
	// iterate over required part index ids and decide which ones to push through
	for (ULONG ul = 0; ul < ulPartIndexIds; ul++)
	{
		ULONG part_idx_id = *((*pdrgpul)[ul]);
		GPOS_ASSERT(ppimReqd->Contains(part_idx_id));

		// if part index id is defined in child, push it to the child
		if (ppartinfo->FContainsScanId(part_idx_id))
		{
			// push requirements to child node
			ppimResult->AddRequiredPartPropagation(ppimReqd, part_idx_id, CPartIndexMap::EppraPreservePropagators);
			if (CPhysical::EppcAllowed == eppcPropogate)
			{
				// for some logical operators such as limit while we push the part index map, we cannot push the constraints
				// since they are NOT semantically equivalent. So only push the constraints when the operator asks this
				// utility function to do so
				(void) ppfmResult->FCopyPartFilter(mp, part_idx_id, ppfmReqd);
			}
		}
	}
	
	pdrgpul->Release();

	return GPOS_NEW(mp) CPartitionPropagationSpec(ppimResult, ppfmResult);
}
Esempio n. 7
0
//---------------------------------------------------------------------------
//	@function:
//		CLogicalSetOp::PpartinfoDerive
//
//	@doc:
//		Derive partition consumer info
//
//---------------------------------------------------------------------------
CPartInfo *
CLogicalSetOp::PpartinfoDerive
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl
	)
	const
{
	const ULONG ulArity = exprhdl.UlArity();
	GPOS_ASSERT(0 < ulArity);

	// start with the part info of the first child
	CPartInfo *ppartinfo = exprhdl.Pdprel(0 /*ulChildIndex*/)->Ppartinfo();
	ppartinfo->AddRef();

	for (ULONG ul = 1; ul < ulArity; ul++)
	{
		CPartInfo *ppartinfoChild = exprhdl.Pdprel(ul)->Ppartinfo();
		GPOS_ASSERT(NULL != ppartinfoChild);

		DrgPcr *pdrgpcrInput = (*m_pdrgpdrgpcrInput)[ul];
		GPOS_ASSERT(pdrgpcrInput->UlLength() == m_pdrgpcrOutput->UlLength());

		CPartInfo *ppartinfoRemapped = ppartinfoChild->PpartinfoWithRemappedKeys(pmp, pdrgpcrInput, m_pdrgpcrOutput);
		CPartInfo *ppartinfoCombined = CPartInfo::PpartinfoCombine(pmp, ppartinfo, ppartinfoRemapped);
		ppartinfoRemapped->Release();

		ppartinfo->Release();
		ppartinfo = ppartinfoCombined;
	}

	return ppartinfo;
}
//---------------------------------------------------------------------------
//	@function:
//		CLogicalDynamicGetBase::PpartinfoDerive
//
//	@doc:
//		Derive partition consumer info
//
//---------------------------------------------------------------------------
CPartInfo *
CLogicalDynamicGetBase::PpartinfoDerive
	(
	IMemoryPool *pmp,
	CExpressionHandle & // exprhdl
	)
	const
{
	IMDId *pmdid = m_ptabdesc->Pmdid();
	pmdid->AddRef();
	m_pdrgpdrgpcrPart->AddRef();
	m_ppartcnstrRel->AddRef(); 
	
	CPartInfo *ppartinfo = GPOS_NEW(pmp) CPartInfo(pmp);
	ppartinfo->AddPartConsumer(pmp, m_ulScanId, pmdid, m_pdrgpdrgpcrPart, m_ppartcnstrRel);
	
	return ppartinfo;
}
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalPartitionSelector::PppsRequired
//
//	@doc:
//		Compute required partition propagation of the n-th child
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysicalPartitionSelector::PppsRequired
	(
	IMemoryPool *mp,
	CExpressionHandle & exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	ULONG
#ifdef GPOS_DEBUG
	child_index
#endif // GPOS_DEBUG
	,
	CDrvdProp2dArray *, //pdrgpdpCtxt,
	ULONG //ulOptReq
	)
{
	GPOS_ASSERT(0 == child_index);
	GPOS_ASSERT(NULL != pppsRequired);

	CPartIndexMap *ppimInput = pppsRequired->Ppim();
	CPartFilterMap *ppfmInput = pppsRequired->Ppfm();

	ULongPtrArray *pdrgpulInputScanIds = ppimInput->PdrgpulScanIds(mp);

	CPartIndexMap *ppim = GPOS_NEW(mp) CPartIndexMap(mp);
	CPartFilterMap *ppfm = GPOS_NEW(mp) CPartFilterMap(mp);

	CPartInfo *ppartinfo = exprhdl.GetRelationalProperties(0)->Ppartinfo();

	const ULONG ulScanIds = pdrgpulInputScanIds->Size();

	for (ULONG ul = 0; ul < ulScanIds; ul++)
	{
		ULONG scan_id = *((*pdrgpulInputScanIds)[ul]);
		ULONG ulExpectedPropagators = ppimInput->UlExpectedPropagators(scan_id);

		if (scan_id == m_scan_id)
		{
			// partition propagation resolved - do not need to require from children
			continue;
		}

		if (!ppartinfo->FContainsScanId(scan_id) && ppartinfo->FContainsScanId(m_scan_id))
		{
		    // dynamic scan for the required id not defined below, but the current one is: do not push request down
			continue;
		}

		IMDId *mdid = ppimInput->GetRelMdId(scan_id);
		CPartKeysArray *pdrgppartkeys = ppimInput->Pdrgppartkeys(scan_id);
		UlongToPartConstraintMap *ppartcnstrmap = ppimInput->Ppartcnstrmap(scan_id);
		CPartConstraint *ppartcnstr = ppimInput->PpartcnstrRel(scan_id);
		CPartIndexMap::EPartIndexManipulator epim = ppimInput->Epim(scan_id);
		mdid->AddRef();
		pdrgppartkeys->AddRef();
		ppartcnstrmap->AddRef();
		ppartcnstr->AddRef();

		ppim->Insert(scan_id, ppartcnstrmap, epim, ulExpectedPropagators, mdid, pdrgppartkeys, ppartcnstr);
		(void) ppfm->FCopyPartFilter(m_mp, scan_id, ppfmInput);
	}

	// cleanup
	pdrgpulInputScanIds->Release();

	return GPOS_NEW(mp) CPartitionPropagationSpec(ppim, ppfm);
}
Esempio n. 10
0
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalPartitionSelector::PppsRequired
//
//	@doc:
//		Compute required partition propagation of the n-th child
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysicalPartitionSelector::PppsRequired
	(
	IMemoryPool *pmp,
	CExpressionHandle & exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	ULONG
#ifdef GPOS_DEBUG
	ulChildIndex
#endif // GPOS_DEBUG
	,
	DrgPdp *, //pdrgpdpCtxt,
	ULONG //ulOptReq
	)
{
	GPOS_ASSERT(0 == ulChildIndex);
	GPOS_ASSERT(NULL != pppsRequired);

	CPartIndexMap *ppimInput = pppsRequired->Ppim();
	CPartFilterMap *ppfmInput = pppsRequired->Ppfm();

	DrgPul *pdrgpulInputScanIds = ppimInput->PdrgpulScanIds(pmp);

	CPartIndexMap *ppim = GPOS_NEW(pmp) CPartIndexMap(pmp);
	CPartFilterMap *ppfm = GPOS_NEW(pmp) CPartFilterMap(pmp);

	CPartInfo *ppartinfo = exprhdl.Pdprel(0)->Ppartinfo();

	const ULONG ulScanIds = pdrgpulInputScanIds->UlLength();

	for (ULONG ul = 0; ul < ulScanIds; ul++)
	{
		ULONG ulScanId = *((*pdrgpulInputScanIds)[ul]);
		ULONG ulExpectedPropagators = ppimInput->UlExpectedPropagators(ulScanId);

		if (ulScanId == m_ulScanId)
		{
			// partition propagation resolved - do not need to require from children
			continue;
		}

		if (!ppartinfo->FContainsScanId(ulScanId) && ppartinfo->FContainsScanId(m_ulScanId))
		{
		    // dynamic scan for the required id not defined below, but the current one is: do not push request down
			continue;
		}

		IMDId *pmdid = ppimInput->PmdidRel(ulScanId);
		DrgPpartkeys *pdrgppartkeys = ppimInput->Pdrgppartkeys(ulScanId);
		PartCnstrMap *ppartcnstrmap = ppimInput->Ppartcnstrmap(ulScanId);
		CPartConstraint *ppartcnstr = ppimInput->PpartcnstrRel(ulScanId);
		CPartIndexMap::EPartIndexManipulator epim = ppimInput->Epim(ulScanId);
		pmdid->AddRef();
		pdrgppartkeys->AddRef();
		ppartcnstrmap->AddRef();
		ppartcnstr->AddRef();

		ppim->Insert(ulScanId, ppartcnstrmap, epim, ulExpectedPropagators, pmdid, pdrgppartkeys, ppartcnstr);
		(void) ppfm->FCopyPartFilter(m_pmp, ulScanId, ppfmInput);
	}

	// cleanup
	pdrgpulInputScanIds->Release();

	return GPOS_NEW(pmp) CPartitionPropagationSpec(ppim, ppfm);
}
Esempio n. 11
0
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalNLJoin::PppsRequiredNLJoinChild
//
//	@doc:
//		Compute required partition propagation of the n-th child
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysicalNLJoin::PppsRequiredNLJoinChild
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	ULONG ulChildIndex,
	DrgPdp *, //pdrgpdpCtxt,
	ULONG ulOptReq
	)
{
	GPOS_ASSERT(NULL != pppsRequired);

	if (1 == ulOptReq)
	{
		// request (1): push partition propagation requests to join's children,
		// do not consider possible dynamic partition elimination using join predicate here,
		// this is handled by optimization request (0) below
		return CPhysical::PppsRequiredPushThruNAry(pmp, exprhdl, pppsRequired, ulChildIndex);
	}
	GPOS_ASSERT(0 == ulOptReq);

	CPartIndexMap *ppim = pppsRequired->Ppim();
	CPartFilterMap *ppfm = pppsRequired->Ppfm();

	DrgPul *pdrgpul = ppim->PdrgpulScanIds(pmp);
	
	CPartIndexMap *ppimResult = GPOS_NEW(pmp) CPartIndexMap(pmp);
	CPartFilterMap *ppfmResult = GPOS_NEW(pmp) CPartFilterMap(pmp);

	CPartInfo *ppartinfoOuter = exprhdl.Pdprel(0)->Ppartinfo();

	CColRefSet *pcrsOutputOuter = exprhdl.Pdprel(0)->PcrsOutput();
	CColRefSet *pcrsOutputInner = exprhdl.Pdprel(1)->PcrsOutput();

	const ULONG ulPartIndexIds = pdrgpul->UlLength();
		
	for (ULONG ul = 0; ul < ulPartIndexIds; ul++)
	{
		ULONG ulPartIndexId = *((*pdrgpul)[ul]);
		if (ppfm->FContainsScanId(ulPartIndexId))
		{
			GPOS_ASSERT(NULL != ppfm->Pexpr(ulPartIndexId));
			// a selection-based propagation request pushed from above: do not propagate any
			// further as the join will reduce cardinality and thus may select more partitions
			// for scanning
			continue;
		}
		
		BOOL fOuterPartConsumer = ppartinfoOuter->FContainsScanId(ulPartIndexId);

		// in order to find interesting join predicates that can be used for DPE,
		// one side of the predicate must be the partition key, while the other side must only contain
		// references from the join child that does not have the partition consumer
		CColRefSet *pcrsAllowedRefs = pcrsOutputOuter;
		if (fOuterPartConsumer)
		{
			pcrsAllowedRefs = pcrsOutputInner;
		}

		if (0 == ulChildIndex && fOuterPartConsumer)
		{
			// always push through required partition propagation for consumers on the
			// outer side of the nested loop join
			DrgPpartkeys *pdrgppartkeys = ppartinfoOuter->PdrgppartkeysByScanId(ulPartIndexId);
			GPOS_ASSERT(NULL != pdrgppartkeys);
			pdrgppartkeys->AddRef();

			ppimResult->AddRequiredPartPropagation(ppim, ulPartIndexId, CPartIndexMap::EppraPreservePropagators, pdrgppartkeys);
		}
		else
		{
			// check if there is an interesting condition involving the partition key
			CExpression *pexprScalar = exprhdl.PexprScalarChild(2 /*ulChildIndex*/);
			AddFilterOnPartKey(pmp, true /*fNLJoin*/, pexprScalar, ppim, ppfm, ulChildIndex, ulPartIndexId, fOuterPartConsumer, ppimResult, ppfmResult, pcrsAllowedRefs);
		}
	}
	
	pdrgpul->Release();

	return GPOS_NEW(pmp) CPartitionPropagationSpec(ppimResult, ppfmResult);
}
Esempio n. 12
0
//---------------------------------------------------------------------------
//	@function:
//		CPhysicalHashJoin::PppsRequiredCompute
//
//	@doc:
//		Compute required partition propagation of the n-th child
//
//---------------------------------------------------------------------------
CPartitionPropagationSpec *
CPhysicalHashJoin::PppsRequiredCompute
	(
	IMemoryPool *pmp,
	CExpressionHandle &exprhdl,
	CPartitionPropagationSpec *pppsRequired,
	ULONG ulChildIndex
	)
{
	CPartIndexMap *ppim = pppsRequired->Ppim();
	CPartFilterMap *ppfm = pppsRequired->Ppfm();

	DrgPul *pdrgpul = ppim->PdrgpulScanIds(pmp);

	CPartIndexMap *ppimResult = GPOS_NEW(pmp) CPartIndexMap(pmp);
	CPartFilterMap *ppfmResult = GPOS_NEW(pmp) CPartFilterMap(pmp);

	// get outer partition consumers
	CPartInfo *ppartinfo = exprhdl.Pdprel(0)->Ppartinfo();

	CColRefSet *pcrsOutputOuter = exprhdl.Pdprel(0)->PcrsOutput();
	CColRefSet *pcrsOutputInner = exprhdl.Pdprel(1)->PcrsOutput();

	const ULONG ulPartIndexIds = pdrgpul->UlLength();

	for (ULONG ul = 0; ul < ulPartIndexIds; ul++)
	{
		ULONG ulPartIndexId = *((*pdrgpul)[ul]);

		if (ppfm->FContainsScanId(ulPartIndexId))
		{
			GPOS_ASSERT(NULL != ppfm->Pexpr(ulPartIndexId));
			// a selection-based propagation request pushed from above: do not propagate any
			// further as the join will reduce cardinality and thus may select more partitions
			// for scanning
			continue;
		}
		
		BOOL fOuterPartConsumer = ppartinfo->FContainsScanId(ulPartIndexId);

		// in order to find interesting join predicates that can be used for DPE,
		// one side of the predicate must be the partition key, while the other side must only contain
		// references from the join child that does not have the partition consumer
		CColRefSet *pcrsAllowedRefs = pcrsOutputOuter;
		if (fOuterPartConsumer)
		{
			pcrsAllowedRefs = pcrsOutputInner;
		}

		if (1 == ulChildIndex && !fOuterPartConsumer)
		{
			// always push through required partition propagation for consumers on the
			// inner side of the hash join
			DrgPpartkeys *pdrgppartkeys = exprhdl.Pdprel(1 /*ulChildIndex*/)->Ppartinfo()->PdrgppartkeysByScanId(ulPartIndexId);
			GPOS_ASSERT(NULL != pdrgppartkeys);
			pdrgppartkeys->AddRef();

			ppimResult->AddRequiredPartPropagation(ppim, ulPartIndexId, CPartIndexMap::EppraPreservePropagators, pdrgppartkeys);
		}
		else
		{	
			// look for a filter on the part key
			CExpression *pexprScalar = exprhdl.PexprScalarChild(2 /*ulChildIndex*/);
			AddFilterOnPartKey(pmp, false /*fNLJoin*/, pexprScalar, ppim, ppfm, ulChildIndex, ulPartIndexId, fOuterPartConsumer, ppimResult, ppfmResult, pcrsAllowedRefs);
		}
	}

	pdrgpul->Release();

	return GPOS_NEW(pmp) CPartitionPropagationSpec(ppimResult, ppfmResult);
}