Exemplo n.º 1
0
//---------------------------------------------------------------------------
//	@function:
//		CXformCollapseGbAgg::Transform
//
//	@doc:
//		Actual transformation to collapse two cascaded group by operators;
//		if the top Gb grouping columns are subset of bottom Gb grouping
//		columns AND both Gb operators do not define agg functions, we can
//		remove the bottom group by operator
//
//
//---------------------------------------------------------------------------
void
CXformCollapseGbAgg::Transform
	(
	CXformContext *pxfctxt,
	CXformResult *pxfres,
	CExpression *pexpr
	)
	const
{
	GPOS_ASSERT(NULL != pxfctxt);
	GPOS_ASSERT(NULL != pxfres);
	GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
	GPOS_ASSERT(FCheckPattern(pexpr));

	IMemoryPool *pmp = pxfctxt->Pmp();

	// extract components
	CLogicalGbAgg *popTopGbAgg = CLogicalGbAgg::PopConvert(pexpr->Pop());
	GPOS_ASSERT(0 < popTopGbAgg->Pdrgpcr()->UlLength());
	GPOS_ASSERT(popTopGbAgg->FGlobal());

	CExpression *pexprRelational = (*pexpr)[0];
	CExpression *pexprTopProjectList = (*pexpr)[1];

	CLogicalGbAgg *popBottomGbAgg = CLogicalGbAgg::PopConvert(pexprRelational->Pop());
	CExpression *pexprChild = (*pexprRelational)[0];
	CExpression *pexprBottomProjectList = (*pexprRelational)[1];

	if (!popBottomGbAgg->FGlobal())
	{
		// bottom GbAgg must be global to prevent xform from getting applied to splitted GbAggs
		return;
	}

	if (0 < pexprTopProjectList->UlArity() || 0 < pexprBottomProjectList->UlArity())
	{
		// exit if any of the Gb operators has an aggregate function
		return;
	}

#ifdef GPOS_DEBUG
	// for two cascaded GbAgg ops with no agg functions, top grouping
	// columns must be a subset of bottom grouping columns
	CColRefSet *pcrsTopGrpCols = GPOS_NEW(pmp) CColRefSet(pmp, popTopGbAgg->Pdrgpcr());
	CColRefSet *pcrsBottomGrpCols = GPOS_NEW(pmp) CColRefSet(pmp, popBottomGbAgg->Pdrgpcr());
	GPOS_ASSERT(pcrsBottomGrpCols->FSubset(pcrsTopGrpCols));

	pcrsTopGrpCols->Release();
	pcrsBottomGrpCols->Release();
#endif // GPOS_DEBUG

	pexprChild->AddRef();
	CExpression *pexprSelect = CUtils::PexprLogicalSelect(pmp, pexprChild, CPredicateUtils::PexprConjunction(pmp, NULL /*pdrgpexpr*/));

	popTopGbAgg->AddRef();
	pexprTopProjectList->AddRef();
	CExpression *pexprGbAggNew = GPOS_NEW(pmp) CExpression(pmp, popTopGbAgg, pexprSelect, pexprTopProjectList);

	pxfres->Add(pexprGbAggNew);
}
Exemplo n.º 2
0
//---------------------------------------------------------------------------
//	@function:
//		CQueryContext::MapComputedToUsedCols
//
//	@doc:
//		Walk the expression and add the mapping between computed column
//		and its used columns
//
//---------------------------------------------------------------------------
void
CQueryContext::MapComputedToUsedCols
(
    CColumnFactory *pcf,
    CExpression *pexpr
)
{
    GPOS_ASSERT(NULL != pexpr);

    if (COperator::EopLogicalProject == pexpr->Pop()->Eopid())
    {
        CExpression *pexprPrL = (*pexpr)[1];

        const ULONG ulArity = pexprPrL->UlArity();
        for (ULONG ul = 0; ul < ulArity; ul++)
        {
            CExpression *pexprPrEl = (*pexprPrL)[ul];
            pcf->AddComputedToUsedColsMap(pexprPrEl);
        }
    }

    // process children
    const ULONG ulChildren = pexpr->UlArity();
    for (ULONG ul = 0; ul < ulChildren; ul++)
    {
        MapComputedToUsedCols(pcf, (*pexpr)[ul]);
    }
}
Exemplo n.º 3
0
//---------------------------------------------------------------------------
//	@function:
//		CCNFConverterTest::EresUnittest_Basic
//
//	@doc:
//		Basic test
//
//---------------------------------------------------------------------------
GPOS_RESULT
CCNFConverterTest::EresUnittest_Basic()
{
	CAutoMemoryPool amp;
	IMemoryPool *pmp = amp.Pmp();

	// setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(pmp, CMDCache::Pcache(), CTestUtils::m_sysidDefault, pmdp);

	typedef CExpression *(*Pfpexpr)(IMemoryPool*);
	Pfpexpr rgpf[] =
		{
		CTestUtils::PexprLogicalSelectWithNestedAnd,
		CTestUtils::PexprLogicalSelectWithNestedOr,
		CTestUtils::PexprLogicalSelectWithEvenNestedNot,
		CTestUtils::PexprLogicalSelectWithOddNestedNot,
		CTestUtils::PexprLogicalSelectWithNestedAndOrNot
		};

	for (ULONG i = 0; i < GPOS_ARRAY_SIZE(rgpf); i++)
	{
		// install opt context in TLS
		CAutoOptCtxt aoc
						(
						pmp,
						&mda,
						NULL,  /* pceeval */
						CTestUtils::Pcm(pmp)
						);

		// generate expression
		CExpression *pexpr = rgpf[i](pmp);
		CExpression *pexprPreprocessed = CExpressionPreprocessor::PexprPreprocess(pmp, pexpr);

		CWStringDynamic str(pmp);
		COstreamString oss(&str);

		oss	<< std::endl << "SCALAR EXPR:" << std::endl << *(*pexpr)[1] << std::endl;
		GPOS_TRACE(str.Wsz());
		str.Reset();

		if (1 < pexprPreprocessed->UlArity())
		{
			CExpression *pexprCNF = CCNFConverter::Pexpr2CNF(pmp, (*pexprPreprocessed)[1]);
			oss	<< std::endl << "CNF REPRESENTATION:" << std::endl << *pexprCNF << std::endl;
			GPOS_TRACE(str.Wsz());
			str.Reset();
			pexprCNF->Release();
		}

		pexpr->Release();
		pexprPreprocessed->Release();
	}

	return GPOS_OK;
}
Exemplo n.º 4
0
//---------------------------------------------------------------------------
//	@function:
//		CCNFConverter::PexprNot2CNF
//
//	@doc:
//		Convert a NOT tree into CNF
//
//---------------------------------------------------------------------------
CExpression *
CCNFConverter::PexprNot2CNF
	(
	IMemoryPool *pmp,
	CExpression *pexpr
	)
{
	GPOS_ASSERT(NULL != pmp);
	GPOS_ASSERT(NULL != pexpr);
	GPOS_ASSERT(1 == pexpr->UlArity());

	CExpression *pexprNotChild = (*pexpr)[0];
	if (!FScalarBoolOp(pexprNotChild))
	{
		pexpr->AddRef();
		return pexpr;
	}

	CScalarBoolOp::EBoolOperator eboolopChild = CScalarBoolOp::PopConvert(pexprNotChild->Pop())->Eboolop();

	// apply DeMorgan laws

	// NOT(NOT(A)) ==> A
	if (CScalarBoolOp::EboolopNot == eboolopChild)
	{
		return Pexpr2CNF(pmp, (*pexprNotChild)[0]);
	}

	// Not child must be either an AND or an OR

	// NOT(A AND B) ==> NOT(A) OR  NOT(B)
	// NOT(A OR  B) ==> NOT(A) AND NOT(B)
	DrgPexpr *pdrgpexpr = GPOS_NEW(pmp) DrgPexpr(pmp);
	const ULONG ulArity = pexprNotChild->UlArity();
	for (ULONG ul = 0; ul < ulArity; ul++)
	{
		(*pexprNotChild)[ul]->AddRef();
		pdrgpexpr->Append(CUtils::PexprNegate(pmp, (*pexprNotChild)[ul]));
	}

	CScalarBoolOp::EBoolOperator eboolop = CScalarBoolOp::EboolopAnd;
	if (CScalarBoolOp::EboolopAnd == eboolopChild)
	{
		eboolop = CScalarBoolOp::EboolopOr;
	}

	CExpression *pexprScalarBoolOp =  CUtils::PexprScalarBoolOp(pmp, eboolop, pdrgpexpr);
	CExpression *pexprResult = Pexpr2CNF(pmp, pexprScalarBoolOp);
	pexprScalarBoolOp->Release();

	return pexprResult;
}
Exemplo n.º 5
0
//---------------------------------------------------------------------------
//	@function:
//		CConstraint::PcnstrFromScalarArrayCmp
//
//	@doc:
//		Create constraint from scalar array comparison expression
//
//---------------------------------------------------------------------------
CConstraint *
CConstraint::PcnstrFromScalarArrayCmp
	(
	IMemoryPool *pmp,
	CExpression *pexpr,
	CColRef *pcr
	)
{
	GPOS_ASSERT(NULL != pexpr);
	GPOS_ASSERT(CUtils::FScalarArrayCmp(pexpr));

	CScalarArrayCmp *popScArrayCmp = CScalarArrayCmp::PopConvert(pexpr->Pop());
	CScalarArrayCmp::EArrCmpType earrccmpt = popScArrayCmp->Earrcmpt();

	if ((CScalarArrayCmp::EarrcmpAny == earrccmpt  || CScalarArrayCmp::EarrcmpAll == earrccmpt) &&
		CPredicateUtils::FCompareIdentToConstArray(pexpr))
	{
		// column
#ifdef GPOS_DEBUG
		CScalarIdent *popScId = CScalarIdent::PopConvert((*pexpr)[0]->Pop());
		GPOS_ASSERT (pcr == (CColRef *) popScId->Pcr());
#endif // GPOS_DEBUG

		// get comparison type
		IMDType::ECmpType ecmpt = CUtils::Ecmpt(popScArrayCmp->PmdidOp());

		CExpression *pexprArray = (*pexpr)[1];
		DrgPcnstr *pdrgpcnstr = GPOS_NEW(pmp) DrgPcnstr(pmp);
		const ULONG ulArity = pexprArray->UlArity();
		for (ULONG ul = 0; ul < ulArity; ul++)
		{
			GPOS_ASSERT(CUtils::FScalarConst((*pexprArray)[ul]) && "expecting a constant");

			CScalarConst *popScConst = CScalarConst::PopConvert((*pexprArray)[ul]->Pop());
			CConstraintInterval *pci =  CConstraintInterval::PciIntervalFromColConstCmp(pmp, pcr, ecmpt, popScConst);
			pdrgpcnstr->Append(pci);
		}

		if (earrccmpt == CScalarArrayCmp::EarrcmpAny)
		{
			// predicate is of the form 'A IN (1,2,3)'
			// return a disjunction of ranges {[1,1], [2,2], [3,3]}
			return GPOS_NEW(pmp) CConstraintDisjunction(pmp, pdrgpcnstr);
		}

		// predicate is of the form 'A NOT IN (1,2,3)'
		// return a conjunctive negation on {[1,1], [2,2], [3,3]}
		return GPOS_NEW(pmp) CConstraintConjunction(pmp, pdrgpcnstr);
	}

	return NULL;
}
//---------------------------------------------------------------------------
//	@function:
//		CXformGbAggDedup2StreamAggDedup::Transform
//
//	@doc:
//		Actual transformation
//
//---------------------------------------------------------------------------
void
CXformGbAggDedup2StreamAggDedup::Transform
	(
	CXformContext *pxfctxt,
	CXformResult *pxfres,
	CExpression *pexpr
	)
	const
{
	GPOS_ASSERT(NULL != pxfctxt);
	GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
	GPOS_ASSERT(FCheckPattern(pexpr));
	IMemoryPool *pmp = pxfctxt->Pmp();

	// extract components
	CExpression *pexprRel = (*pexpr)[0];
	CExpression *pexprScalar = (*pexpr)[1];
	GPOS_ASSERT(0 == pexprScalar->UlArity());

	// addref children
	pexprRel->AddRef();
	pexprScalar->AddRef();

	CLogicalGbAggDeduplicate *popAggDedup = CLogicalGbAggDeduplicate::PopConvert(pexpr->Pop());
	DrgPcr *pdrgpcr = popAggDedup->Pdrgpcr();
	pdrgpcr->AddRef();

	DrgPcr *pdrgpcrKeys = popAggDedup->PdrgpcrKeys();
	pdrgpcrKeys->AddRef();

	// create alternative expression
	CExpression *pexprAlt =
		GPOS_NEW(pmp) CExpression
			(
			pmp,
			GPOS_NEW(pmp) CPhysicalStreamAggDeduplicate
						(
						pmp,
						pdrgpcr,
						popAggDedup->PdrgpcrMinimal(),
						popAggDedup->Egbaggtype(),
						pdrgpcrKeys,
						popAggDedup->FGeneratesDuplicates(),
						CXformUtils::FMultiStageAgg(pexpr)
						),
			pexprRel,
			pexprScalar
			);

	// add alternative to transformation result
	pxfres->Add(pexprAlt);
}
Exemplo n.º 7
0
//---------------------------------------------------------------------------
//	@function:
//		CXformSimplifyGbAgg::FDropGbAgg
//
//	@doc:
//		Return true if GbAgg operator can be dropped because grouping
//		columns include a key
//
//---------------------------------------------------------------------------
BOOL
CXformSimplifyGbAgg::FDropGbAgg
	(
	IMemoryPool *pmp,
	CExpression *pexpr,
	CXformResult *pxfres
	)
{
	CLogicalGbAgg *popAgg = CLogicalGbAgg::PopConvert(pexpr->Pop());
	CExpression *pexprRelational = (*pexpr)[0];
	CExpression *pexprProjectList = (*pexpr)[1];

	if (0 < pexprProjectList->UlArity())
	{
		// GbAgg cannot be dropped if Agg functions are computed
		return false;
	}

	CKeyCollection *pkc = CDrvdPropRelational::Pdprel(pexprRelational->PdpDerive())->Pkc();
	if (NULL == pkc)
	{
		// relational child does not have key
		return false;
	}

	const ULONG ulKeys = pkc->UlKeys();
	BOOL fDrop = false;
	for (ULONG ul = 0; !fDrop && ul < ulKeys; ul++)
	{
		DrgPcr *pdrgpcrKey = pkc->PdrgpcrKey(pmp, ul);
		CColRefSet *pcrs = GPOS_NEW(pmp) CColRefSet(pmp, pdrgpcrKey);
		pdrgpcrKey->Release();

		CColRefSet *pcrsGrpCols = GPOS_NEW(pmp) CColRefSet(pmp);
		pcrsGrpCols->Include(popAgg->Pdrgpcr());
		BOOL fGrpColsHasKey = pcrsGrpCols->FSubset(pcrs);

		pcrs->Release();
		pcrsGrpCols->Release();
		if (fGrpColsHasKey)
		{
			// Gb operator can be dropped
			pexprRelational->AddRef();
			CExpression *pexprResult =
				CUtils::PexprLogicalSelect(pmp, pexprRelational, CPredicateUtils::PexprConjunction(pmp, NULL));
			pxfres->Add(pexprResult);
			fDrop = true;
		}
	}

	return fDrop;
}
Exemplo n.º 8
0
//---------------------------------------------------------------------------
//	@function:
//		CJoinOrderTest::EresUnittest_Expand
//
//	@doc:
//		Simple expansion test
//
//---------------------------------------------------------------------------
GPOS_RESULT
CJoinOrderTest::EresUnittest_Expand()
{
	CAutoMemoryPool amp;
	IMemoryPool *pmp = amp.Pmp();

	// setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(pmp, CMDCache::Pcache(), CTestUtils::m_sysidDefault, pmdp);
	
	// install opt context in TLS
	CAutoOptCtxt aoc
				(
				pmp,
				&mda,
				NULL,  /* pceeval */
				CTestUtils::Pcm(pmp)
				);

	// build test case
	CExpression *pexpr = CTestUtils::PexprLogicalNAryJoin(pmp);
	DrgPexpr *pdrgpexpr = GPOS_NEW(pmp) DrgPexpr(pmp);
	ULONG ulArity = pexpr->UlArity();
	for (ULONG ul = 0; ul < ulArity - 1; ul++)
	{
		CExpression *pexprChild = (*pexpr)[ul];
		pexprChild->AddRef();
		pdrgpexpr->Append(pexprChild);
	}

	DrgPexpr *pdrgpexprConj = CPredicateUtils::PdrgpexprConjuncts(pmp, (*pexpr)[ulArity - 1]);

	// add predicates selectively to trigger special case of cross join
	DrgPexpr *pdrgpexprTest = GPOS_NEW(pmp) DrgPexpr(pmp);	
	for (ULONG ul = 0; ul < pdrgpexprConj->UlLength() - 1; ul++)
	{
		CExpression *pexprConjunct = (*pdrgpexprConj)[ul];
		pexprConjunct->AddRef();
		pdrgpexprTest->Append(pexprConjunct);
	}
	
	pdrgpexprConj->Release();

	// single-table predicate
	CColRefSet *pcrsOutput = CDrvdPropRelational::Pdprel((*pdrgpexpr)[ulArity - 2]->PdpDerive())->PcrsOutput();
	CExpression *pexprSingleton = CUtils::PexprScalarEqCmp(pmp, pcrsOutput->PcrAny(), pcrsOutput->PcrAny());
	
	pdrgpexprTest->Append(pexprSingleton);
	
	CJoinOrder jo(pmp, pdrgpexpr, pdrgpexprTest);
	CExpression *pexprResult = jo.PexprExpand();
	{
		CAutoTrace at(pmp);
		at.Os() << std::endl << "INPUT:" << std::endl << *pexpr << std::endl;
		at.Os() << std::endl << "OUTPUT:" << std::endl << *pexprResult << std::endl;
	}

	CRefCount::SafeRelease(pexprResult);

	pexpr->Release();

	return GPOS_OK;
}
Exemplo n.º 9
0
//---------------------------------------------------------------------------
//	@function:
//		CStatsPredUtils::ProcessArrayCmp
//
//	@doc:
//		Extract statistics filtering information from scalar array comparison
//---------------------------------------------------------------------------
void
CStatsPredUtils::ProcessArrayCmp
	(
	IMemoryPool *pmp,
	CExpression *pexprPred,
	DrgPstatspred *pdrgpstatspred
	)
{
	GPOS_ASSERT(NULL != pdrgpstatspred);
	GPOS_ASSERT(NULL != pexprPred);
	GPOS_ASSERT(2 == pexprPred->UlArity());

	CScalarArrayCmp *popScArrayCmp = CScalarArrayCmp::PopConvert(pexprPred->Pop());
	CExpression *pexprLeft = (*pexprPred)[0];
	CExpression *pexprRight = (*pexprPred)[1];

	BOOL fCompareToConst = ((COperator::EopScalarIdent == pexprLeft->Pop()->Eopid())
							&& (COperator::EopScalarArray == pexprRight->Pop()->Eopid()));

	if (!fCompareToConst)
	{
		// unsupported predicate for stats calculations
		pdrgpstatspred->Append(GPOS_NEW(pmp) CStatsPredUnsupported(ULONG_MAX, CStatsPred::EstatscmptOther));

		return;
	}

	BOOL fAny = (CScalarArrayCmp::EarrcmpAny == popScArrayCmp->Earrcmpt());

	DrgPstatspred *pdrgpstatspredChild = pdrgpstatspred;
	if (fAny)
	{
		pdrgpstatspredChild = GPOS_NEW(pmp) DrgPstatspred(pmp);
	}

	const ULONG ulConstants = pexprRight->UlArity();
	// comparison semantics for statistics purposes is looser than regular comparison.
	CStatsPred::EStatsCmpType escmpt = Estatscmptype(popScArrayCmp->PmdidOp());

	CScalarIdent *popScalarIdent = CScalarIdent::PopConvert(pexprLeft->Pop());
	const CColRef *pcr = popScalarIdent->Pcr();

	if (!CHistogram::FSupportsFilter(escmpt))
	{
		// unsupported predicate for stats calculations
		pdrgpstatspred->Append(GPOS_NEW(pmp) CStatsPredUnsupported(pcr->UlId(), escmpt));

		return;
	}

	for (ULONG ul = 0; ul < ulConstants; ul++)
	{
		CExpression *pexprConst = (*pexprRight)[ul];
		if (COperator::EopScalarConst == pexprConst->Pop()->Eopid())
		{
			CScalarConst *popScalarConst = CScalarConst::PopConvert(pexprConst->Pop());
			IDatum *pdatumLiteral = popScalarConst->Pdatum();
			CStatsPred *pstatspredChild = NULL;
			if (!pdatumLiteral->FStatsComparable(pdatumLiteral))
			{
				// stats calculations on such datums unsupported
				pstatspredChild = GPOS_NEW(pmp) CStatsPredUnsupported(pcr->UlId(), escmpt);
			}
			else
			{
				pstatspredChild = GPOS_NEW(pmp) CStatsPredPoint(pmp, pcr, escmpt, pdatumLiteral);
			}

			pdrgpstatspredChild->Append(pstatspredChild);
		}
	}

	if (fAny)
	{
		CStatsPredDisj *pstatspredOr = GPOS_NEW(pmp) CStatsPredDisj(pdrgpstatspredChild);
		pdrgpstatspred->Append(pstatspredOr);
	}
}
Exemplo n.º 10
0
//---------------------------------------------------------------------------
//	@function:
//		CNormalizer::PexprPullUpProjectElements
//
//	@doc:
//		Pull up project elements from the given projection expression that do not
//		exist in the given used columns set. The pulled up project elements must only
//		use columns that are in the output columns of the parent operator. Returns
//		a new expression that does not have the pulled up project elements. These
//		project elements are appended to the given array.
//
//---------------------------------------------------------------------------
CExpression *
CNormalizer::PexprPullUpProjectElements
	(
	IMemoryPool *pmp,
	CExpression *pexpr,
	CColRefSet *pcrsUsed,
	CColRefSet *pcrsOutput,
	DrgPexpr **ppdrgpexprPrElPullUp	// output: the pulled-up project elements
	)
{
	GPOS_ASSERT(NULL != pexpr);
	GPOS_ASSERT(COperator::EopLogicalProject == pexpr->Pop()->Eopid());
	GPOS_ASSERT(NULL != pcrsUsed);
	GPOS_ASSERT(NULL != ppdrgpexprPrElPullUp);
	GPOS_ASSERT(NULL != *ppdrgpexprPrElPullUp);

	if (2 != pexpr->UlArity())
	{
		// the project's children were not extracted as part of the pattern in this xform
		GPOS_ASSERT(0 == pexpr->UlArity());
		pexpr->AddRef();
		return pexpr;
	}

	DrgPexpr *pdrgpexprPrElNoPullUp = GPOS_NEW(pmp) DrgPexpr(pmp);
	CExpression *pexprPrL = (*pexpr)[1];

	const ULONG ulProjElements = pexprPrL->UlArity();
	for (ULONG ul = 0; ul < ulProjElements; ul++)
	{
		CExpression *pexprPrEl = (*pexprPrL)[ul];
		CScalarProjectElement *popPrEl = CScalarProjectElement::PopConvert(pexprPrEl->Pop());
		CColRef *pcrDefined = popPrEl->Pcr();
		CColRefSet *pcrsUsedByProjElem = CDrvdPropScalar::Pdpscalar(pexprPrEl->PdpDerive())->PcrsUsed();

		// a proj elem can be pulled up only if the defined column is not in
		// pcrsUsed and its used columns are in pcrOutput
		pexprPrEl->AddRef();
		if (!pcrsUsed->FMember(pcrDefined) && pcrsOutput->FSubset(pcrsUsedByProjElem))
		{
			(*ppdrgpexprPrElPullUp)->Append(pexprPrEl);
		}
		else
		{
			pdrgpexprPrElNoPullUp->Append(pexprPrEl);
		}
	}

	CExpression *pexprNew = (*pexpr)[0];
	pexprNew->AddRef();
	if (0 == pdrgpexprPrElNoPullUp->UlLength())
	{
		pdrgpexprPrElNoPullUp->Release();
	}
	else
	{
		// some project elements could not be pulled up - need a project here
		CExpression *pexprPrjList = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CScalarProjectList(pmp), pdrgpexprPrElNoPullUp);
		pexprNew = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CLogicalProject(pmp), pexprNew, pexprPrjList);
	}

	return pexprNew;
}
//---------------------------------------------------------------------------
//	@function:
//		CXformPushDownLeftOuterJoin::Transform
//
//	@doc:
//		Transform LOJ whose outer child is an NAry-join to be a child
//		of NAry-join
//
//		Input:
//			LOJ (a=d)
//				|---NAry-Join (a=b) and (b=c)
//				|     |--A
//				|     |--B
//				|     +--C
//				+--D
//
//		Output:
//			  NAry-Join (a=b) and (b=c)
//				|--B
//				|--C
//				+--LOJ (a=d)
//					|--A
//					+--D
//
//---------------------------------------------------------------------------
void
CXformPushDownLeftOuterJoin::Transform
	(
	CXformContext *pxfctxt,
	CXformResult *pxfres,
	CExpression *pexpr
	)
	const
{
	GPOS_ASSERT(NULL != pxfctxt);
	GPOS_ASSERT(NULL != pxfres);
	GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
	GPOS_ASSERT(FCheckPattern(pexpr));

	IMemoryPool *pmp = pxfctxt->Pmp();

	CExpression *pexprNAryJoin = (*pexpr)[0];
	CExpression *pexprLOJInnerChild = (*pexpr)[1];
	CExpression *pexprLOJScalarChild = (*pexpr)[2];

	CColRefSet *pcrsLOJUsed = CDrvdPropScalar::Pdpscalar(pexprLOJScalarChild->PdpDerive())->PcrsUsed();
	DrgPexpr *pdrgpexprLOJChildren = GPOS_NEW(pmp) DrgPexpr(pmp);
	DrgPexpr *pdrgpexprNAryJoinChildren = GPOS_NEW(pmp) DrgPexpr(pmp);

	const ULONG ulArity = pexprNAryJoin->UlArity();
	CExpression *pexprNAryJoinScalarChild = (*pexprNAryJoin)[ulArity - 1];
	for (ULONG ul = 0 ; ul < ulArity - 1; ul++)
	{
		CExpression *pexprChild = (*pexprNAryJoin)[ul];
		CColRefSet *pcrsOutput = CDrvdPropRelational::Pdprel(pexprChild->PdpDerive())->PcrsOutput();
		pexprChild->AddRef();
		if (!pcrsOutput->FDisjoint(pcrsLOJUsed))
		{
			pdrgpexprLOJChildren->Append(pexprChild);
		}
		else
		{
			pdrgpexprNAryJoinChildren->Append(pexprChild);
		}
	}

	CExpression *pexprLOJOuterChild = (*pdrgpexprLOJChildren)[0];
	if (1 < pdrgpexprLOJChildren->UlLength())
	{
		// collect all relations needed by LOJ outer side into a cross product,
		// normalization at the end of this function takes care of pushing NAry
		// join predicates down
		pdrgpexprLOJChildren->Append(CPredicateUtils::PexprConjunction(pmp, NULL /*pdrgpexpr*/));
		pexprLOJOuterChild = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CLogicalNAryJoin(pmp), pdrgpexprLOJChildren);

		// reconstruct LOJ children and add only the created child
		pdrgpexprLOJChildren = GPOS_NEW(pmp) DrgPexpr(pmp);
		pdrgpexprLOJChildren->Append(pexprLOJOuterChild);
	}

	// continue with rest of LOJ inner and scalar children
	pexprLOJInnerChild->AddRef();
	pdrgpexprLOJChildren->Append(pexprLOJInnerChild);
	pexprLOJScalarChild->AddRef();
	pdrgpexprLOJChildren->Append(pexprLOJScalarChild);

	// build new LOJ
	CExpression *pexprLOJNew = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CLogicalLeftOuterJoin(pmp), pdrgpexprLOJChildren);

	// add new NAry join children
	pdrgpexprNAryJoinChildren->Append(pexprLOJNew);
	pexprNAryJoinScalarChild->AddRef();
	pdrgpexprNAryJoinChildren->Append(pexprNAryJoinScalarChild);

	if (3 > pdrgpexprNAryJoinChildren->UlLength())
	{
		// xform must generate a valid NAry-join expression
		// for example, in the following case we end-up with the same input
		// expression, which should be avoided:
		//
		//	Input:
		//
		//    LOJ (a=c) and (b=c)
		//     |--NAry-Join (a=b)
		//     |   |--A
		//     |   +--B
		//     +--C
		//
		//	Output:
		//
		//	  NAry-Join (true)
		//      +--LOJ (a=c) and (b=c)
		//           |--NAry-Join (a=b)
		//           |    |--A
		//           |    +--B
		//           +--C

		pdrgpexprNAryJoinChildren->Release();
		return;
	}

	// create new NAry join
	CExpression *pexprNAryJoinNew = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CLogicalNAryJoin(pmp), pdrgpexprNAryJoinChildren);

	// normalize resulting expression and add it to xform results
	CExpression *pexprResult = CNormalizer::PexprNormalize(pmp, pexprNAryJoinNew);
	pexprNAryJoinNew->Release();

	pxfres->Add(pexprResult);
}