//--------------------------------------------------------------------------- // @function: // CXformGbAggWithMDQA2Join::PexprTransform // // @doc: // Main transformation driver // //--------------------------------------------------------------------------- CExpression * CXformGbAggWithMDQA2Join::PexprTransform ( IMemoryPool *pmp, CExpression *pexpr ) { // protect against stack overflow during recursion GPOS_CHECK_STACK_SIZE; GPOS_ASSERT(NULL != pmp); GPOS_ASSERT(NULL != pexpr); COperator *pop = pexpr->Pop(); if (COperator::EopLogicalGbAgg == pop->Eopid()) { CExpression *pexprResult = PexprExpandMDQAs(pmp, pexpr); if (NULL != pexprResult) { return pexprResult; } } // recursively process child expressions const ULONG ulArity = pexpr->UlArity(); DrgPexpr *pdrgpexprChildren = GPOS_NEW(pmp) DrgPexpr(pmp); for (ULONG ul = 0; ul < ulArity; ul++) { CExpression *pexprChild = PexprTransform(pmp, (*pexpr)[ul]); pdrgpexprChildren->Append(pexprChild); } pop->AddRef(); return GPOS_NEW(pmp) CExpression(pmp, pop, pdrgpexprChildren); }
//--------------------------------------------------------------------------- // @function: // CDecorrelator::FProcessMaxOneRow // // @doc: // Decorrelate max one row operator // //--------------------------------------------------------------------------- BOOL CDecorrelator::FProcessMaxOneRow ( IMemoryPool *pmp, CExpression *pexpr, BOOL fEqualityOnly, CExpression **ppexprDecorrelated, DrgPexpr *pdrgpexprCorrelations ) { GPOS_ASSERT(NULL != pexpr); COperator *pop = pexpr->Pop(); GPOS_ASSERT(COperator::EopLogicalMaxOneRow == pop->Eopid()); // fail if MaxOneRow expression has outer references if (CUtils::FHasOuterRefs(pexpr)) { return false; } // decorrelate relational child CExpression *pexprRelational = NULL; if (!FProcess(pmp, (*pexpr)[0], fEqualityOnly, &pexprRelational, pdrgpexprCorrelations)) { GPOS_ASSERT(NULL == pexprRelational); return false; } // assemble new project pop->AddRef(); *ppexprDecorrelated = GPOS_NEW(pmp) CExpression(pmp, pop, pexprRelational); return true; }
//--------------------------------------------------------------------------- // @function: // COptimizationContext::FOptimize // // @doc: // Return true if given group expression should be optimized under // given context // //--------------------------------------------------------------------------- BOOL COptimizationContext::FOptimize ( IMemoryPool *mp, CGroupExpression *pgexprParent, CGroupExpression *pgexprChild, COptimizationContext *pocChild, ULONG ulSearchStages ) { COperator *pop = pgexprChild->Pop(); if (CUtils::FPhysicalMotion(pop)) { return FOptimizeMotion(mp, pgexprParent, pgexprChild, pocChild, ulSearchStages); } if (COperator::EopPhysicalSort == pop->Eopid()) { return FOptimizeSort(mp, pgexprParent, pgexprChild, pocChild, ulSearchStages); } if (CUtils::FPhysicalAgg(pop)) { return FOptimizeAgg(mp, pgexprParent, pgexprChild, pocChild, ulSearchStages); } if (CUtils::FNLJoin(pop)) { return FOptimizeNLJoin(mp, pgexprParent, pgexprChild, pocChild, ulSearchStages); } return true; }
//--------------------------------------------------------------------------- // @function: // CPhysicalHashJoin::FNullableHashKey // // @doc: // Check whether a hash key is nullable // //--------------------------------------------------------------------------- BOOL CPhysicalHashJoin::FNullableHashKey ( ULONG ulKey, CColRefSet *pcrsNotNull, BOOL fInner ) const { COperator *pop = NULL; if (fInner) { pop = (*m_pdrgpexprInnerKeys)[ulKey]->Pop(); } else { pop = (*m_pdrgpexprOuterKeys)[ulKey]->Pop(); } EOperatorId eopid = pop->Eopid(); if (COperator::EopScalarIdent == eopid) { const CColRef *pcr = CScalarIdent::PopConvert(pop)->Pcr(); return (!pcrsNotNull->FMember(pcr)); } if (COperator::EopScalarConst == eopid) { return CScalarConst::PopConvert(pop)->Pdatum()->FNull(); } // be conservative for all other scalar expressions where we cannot easily // determine nullability return true; }
//--------------------------------------------------------------------------- // @function: // CExpressionHandle::DerivePlanProps // // @doc: // Derive the properties of the plan carried by attached cost context // //--------------------------------------------------------------------------- void CExpressionHandle::DerivePlanProps ( CDrvdPropCtxtPlan *pdpctxtplan ) { GPOS_ASSERT(NULL != m_pcc); GPOS_ASSERT(NULL != m_pgexpr); GPOS_ASSERT(NULL == m_pdrgpdp); GPOS_ASSERT(NULL == m_pdp); GPOS_CHECK_ABORT; // check if properties have been already derived if (NULL != m_pcc->Pdpplan()) { CopyCostCtxtProps(); return; } GPOS_ASSERT(NULL != pdpctxtplan); // extract children's properties m_pdrgpdp = GPOS_NEW(m_pmp) DrgPdp(m_pmp); const ULONG ulArity = m_pcc->Pdrgpoc()->UlLength(); for (ULONG ul = 0; ul < ulArity; ul++) { COptimizationContext *pocChild = (*m_pcc->Pdrgpoc())[ul]; CDrvdPropPlan *pdpplan = pocChild->PccBest()->Pdpplan(); GPOS_ASSERT(NULL != pdpplan); pdpplan->AddRef(); m_pdrgpdp->Append(pdpplan); // add child props to derivation context CDrvdPropCtxt::AddDerivedProps(pdpplan, pdpctxtplan); } COperator *pop = m_pgexpr->Pop(); if (COperator::EopPhysicalCTEConsumer == pop->Eopid()) { // copy producer plan properties to passed derived plan properties context ULONG ulCTEId = CPhysicalCTEConsumer::PopConvert(pop)->UlCTEId(); CDrvdPropPlan *pdpplan = m_pcc->Poc()->Prpp()->Pcter()->Pdpplan(ulCTEId); if (NULL != pdpplan) { pdpctxtplan->CopyCTEProducerProps(pdpplan, ulCTEId); } } // set the number of expected partition selectors in the context pdpctxtplan->SetExpectedPartitionSelectors(pop, m_pcc); // create/derive local properties m_pdp = Pop()->PdpCreate(m_pmp); m_pdp->Derive(m_pmp, *this, pdpctxtplan); }
//--------------------------------------------------------------------------- // @function: // CDecorrelator::FProcessAssert // // @doc: // Decorrelate assert operator // //--------------------------------------------------------------------------- BOOL CDecorrelator::FProcessAssert ( IMemoryPool *pmp, CExpression *pexpr, BOOL fEqualityOnly, CExpression **ppexprDecorrelated, DrgPexpr *pdrgpexprCorrelations ) { GPOS_ASSERT(NULL != pexpr); COperator *pop = pexpr->Pop(); GPOS_ASSERT(COperator::EopLogicalAssert == pop->Eopid()); CExpression *pexprScalar = (*pexpr)[1]; // fail if assert expression has outer references CColRefSet *pcrsOutput = CDrvdPropRelational::Pdprel((*pexpr)[0]->PdpDerive())->PcrsOutput(); CColRefSet *pcrsUsed = CDrvdPropScalar::Pdpscalar(pexprScalar->PdpDerive())->PcrsUsed(); if (!pcrsOutput->FSubset(pcrsUsed)) { return false; } // decorrelate relational child CExpression *pexprRelational = NULL; if (!FProcess(pmp, (*pexpr)[0], fEqualityOnly, &pexprRelational, pdrgpexprCorrelations)) { GPOS_ASSERT(NULL == pexprRelational); return false; } // assemble new project pop->AddRef(); pexprScalar->AddRef(); *ppexprDecorrelated = GPOS_NEW(pmp) CExpression(pmp, pop, pexprRelational, pexprScalar); return true; }
//--------------------------------------------------------------------------- // @function: // CDistributionSpecHashed::PdshashedExcludeColumns // // @doc: // Return a copy of the distribution spec after excluding the given // columns, return NULL if all distribution expressions are excluded // //--------------------------------------------------------------------------- CDistributionSpecHashed * CDistributionSpecHashed::PdshashedExcludeColumns ( IMemoryPool *pmp, CColRefSet *pcrs ) { GPOS_ASSERT(NULL != pcrs); DrgPexpr *pdrgpexprNew = GPOS_NEW(pmp) DrgPexpr(pmp); const ULONG ulExprs = m_pdrgpexpr->UlLength(); for (ULONG ul = 0; ul < ulExprs; ul++) { CExpression *pexpr = (*m_pdrgpexpr)[ul]; COperator *pop = pexpr->Pop(); if (COperator::EopScalarIdent == pop->Eopid()) { // we only care here about column identifiers, // any more complicated expressions are copied to output const CColRef *pcr = CScalarIdent::PopConvert(pop)->Pcr(); if (pcrs->FMember(pcr)) { continue; } } pexpr->AddRef(); pdrgpexprNew->Append(pexpr); } if (0 == pdrgpexprNew->UlLength()) { pdrgpexprNew->Release(); return NULL; } return GPOS_NEW(pmp) CDistributionSpecHashed(pdrgpexprNew, m_fNullsColocated); }
//--------------------------------------------------------------------------- // @function: // CQueryContext::PqcGenerate // // @doc: // Generate the query context for the given expression and array of // output column ref ids // //--------------------------------------------------------------------------- CQueryContext * CQueryContext::PqcGenerate ( IMemoryPool *pmp, CExpression * pexpr, DrgPul *pdrgpulQueryOutputColRefId, DrgPmdname *pdrgpmdname, BOOL fDeriveStats ) { GPOS_ASSERT(NULL != pexpr && NULL != pdrgpulQueryOutputColRefId); CColRefSet *pcrs = GPOS_NEW(pmp) CColRefSet(pmp); DrgPcr *pdrgpcr = GPOS_NEW(pmp) DrgPcr(pmp); COptCtxt *poptctxt = COptCtxt::PoctxtFromTLS(); CColumnFactory *pcf = poptctxt->Pcf(); GPOS_ASSERT(NULL != pcf); const ULONG ulLen = pdrgpulQueryOutputColRefId->UlLength(); for (ULONG ul = 0; ul < ulLen; ul++) { ULONG *pul = (*pdrgpulQueryOutputColRefId)[ul]; GPOS_ASSERT(NULL != pul); CColRef *pcr = pcf->PcrLookup(*pul); GPOS_ASSERT(NULL != pcr); pcrs->Include(pcr); pdrgpcr->Append(pcr); } COrderSpec *pos = NULL; CExpression *pexprResult = pexpr; COperator *popTop = PopTop(pexpr); if (COperator::EopLogicalLimit == popTop->Eopid()) { // top level operator is a limit, copy order spec to query context pos = CLogicalLimit::PopConvert(popTop)->Pos(); pos->AddRef(); } else { // no order required pos = GPOS_NEW(pmp) COrderSpec(pmp); } CDistributionSpec *pds = NULL; BOOL fDML = CUtils::FLogicalDML(pexpr->Pop()); poptctxt->MarkDMLQuery(fDML); if (fDML) { pds = GPOS_NEW(pmp) CDistributionSpecAny(COperator::EopSentinel); } else { pds = GPOS_NEW(pmp) CDistributionSpecSingleton(CDistributionSpecSingleton::EstMaster); } CRewindabilitySpec *prs = GPOS_NEW(pmp) CRewindabilitySpec(CRewindabilitySpec::ErtNone /*ert*/); CEnfdOrder *peo = GPOS_NEW(pmp) CEnfdOrder(pos, CEnfdOrder::EomSatisfy); // we require satisfy matching on distribution since final query results must be sent to master CEnfdDistribution *ped = GPOS_NEW(pmp) CEnfdDistribution(pds, CEnfdDistribution::EdmSatisfy); CEnfdRewindability *per = GPOS_NEW(pmp) CEnfdRewindability(prs, CEnfdRewindability::ErmSatisfy); CCTEReq *pcter = poptctxt->Pcteinfo()->PcterProducers(pmp); CReqdPropPlan *prpp = GPOS_NEW(pmp) CReqdPropPlan(pcrs, peo, ped, per, pcter); pdrgpmdname->AddRef(); return GPOS_NEW(pmp) CQueryContext(pmp, pexprResult, prpp, pdrgpcr, pdrgpmdname, fDeriveStats); }
//--------------------------------------------------------------------------- // @function: // CQueryContext::PqcGenerate // // @doc: // Generate the query context for the given expression and array of // output column ref ids // //--------------------------------------------------------------------------- CQueryContext * CQueryContext::PqcGenerate ( IMemoryPool *mp, CExpression * pexpr, ULongPtrArray *pdrgpulQueryOutputColRefId, CMDNameArray *pdrgpmdname, BOOL fDeriveStats ) { GPOS_ASSERT(NULL != pexpr && NULL != pdrgpulQueryOutputColRefId); CColRefSet *pcrs = GPOS_NEW(mp) CColRefSet(mp); CColRefArray *colref_array = GPOS_NEW(mp) CColRefArray(mp); COptCtxt *poptctxt = COptCtxt::PoctxtFromTLS(); CColumnFactory *col_factory = poptctxt->Pcf(); GPOS_ASSERT(NULL != col_factory); // Collect required column references (colref_array) const ULONG length = pdrgpulQueryOutputColRefId->Size(); for (ULONG ul = 0; ul < length; ul++) { ULONG *pul = (*pdrgpulQueryOutputColRefId)[ul]; GPOS_ASSERT(NULL != pul); CColRef *colref = col_factory->LookupColRef(*pul); GPOS_ASSERT(NULL != colref); pcrs->Include(colref); colref_array->Append(colref); } // Collect required properties (prpp) at the top level: // By default no sort order requirement is added, unless the root operator in // the input logical expression is a LIMIT. This is because Orca always // attaches top level Sort to a LIMIT node. COrderSpec *pos = NULL; CExpression *pexprResult = pexpr; COperator *popTop = PopTop(pexpr); if (COperator::EopLogicalLimit == popTop->Eopid()) { // top level operator is a limit, copy order spec to query context pos = CLogicalLimit::PopConvert(popTop)->Pos(); pos->AddRef(); } else { // no order required pos = GPOS_NEW(mp) COrderSpec(mp); } CDistributionSpec *pds = NULL; BOOL fDML = CUtils::FLogicalDML(pexpr->Pop()); poptctxt->MarkDMLQuery(fDML); // DML commands do not have distribution requirement. Otherwise the // distribution requirement is Singleton. if (fDML) { pds = GPOS_NEW(mp) CDistributionSpecAny(COperator::EopSentinel); } else { pds = GPOS_NEW(mp) CDistributionSpecSingleton(CDistributionSpecSingleton::EstMaster); } // By default, no rewindability requirement needs to be satisfied at the top level CRewindabilitySpec *prs = GPOS_NEW(mp) CRewindabilitySpec(CRewindabilitySpec::ErtNotRewindable, CRewindabilitySpec::EmhtNoMotion); // Ensure order, distribution and rewindability meet 'satisfy' matching at the top level CEnfdOrder *peo = GPOS_NEW(mp) CEnfdOrder(pos, CEnfdOrder::EomSatisfy); CEnfdDistribution *ped = GPOS_NEW(mp) CEnfdDistribution(pds, CEnfdDistribution::EdmSatisfy); CEnfdRewindability *per = GPOS_NEW(mp) CEnfdRewindability(prs, CEnfdRewindability::ErmSatisfy); // Required CTEs are obtained from the CTEInfo global information in the optimizer context CCTEReq *pcter = poptctxt->Pcteinfo()->PcterProducers(mp); // NB: Partition propagation requirements are not initialized here. They are // constructed later based on derived relation properties (CPartInfo) by // CReqdPropPlan::InitReqdPartitionPropagation(). CReqdPropPlan *prpp = GPOS_NEW(mp) CReqdPropPlan(pcrs, peo, ped, per, pcter); // Finally, create the CQueryContext pdrgpmdname->AddRef(); return GPOS_NEW(mp) CQueryContext(mp, pexprResult, prpp, colref_array, pdrgpmdname, fDeriveStats); }
//--------------------------------------------------------------------------- // @function: // CStatsPredUtils::FCmpColsIgnoreCast // // @doc: // Is the expression a comparison of scalar ident or cast of a scalar ident? // Extract relevant info. // //--------------------------------------------------------------------------- BOOL CStatsPredUtils::FCmpColsIgnoreCast ( CExpression *pexpr, const CColRef **ppcrLeft, CStatsPred::EStatsCmpType *pescmpt, const CColRef **ppcrRight ) { GPOS_ASSERT(NULL != ppcrLeft); GPOS_ASSERT(NULL != ppcrRight); COperator *pop = pexpr->Pop(); BOOL fINDF = CPredicateUtils::FINDF(pexpr); BOOL fIDF = CPredicateUtils::FIDF(pexpr); BOOL fScalarCmp = (COperator::EopScalarCmp == pop->Eopid()); if (!fScalarCmp && !fINDF && !fIDF) { return false; } CExpression *pexprLeft = NULL; CExpression *pexprRight = NULL; if (fINDF) { (*pescmpt) = CStatsPred::EstatscmptINDF; CExpression *pexprIDF = (*pexpr)[0]; pexprLeft = (*pexprIDF)[0]; pexprRight = (*pexprIDF)[1]; } else if (fIDF) { (*pescmpt) = CStatsPred::EstatscmptIDF; pexprLeft = (*pexpr)[0]; pexprRight = (*pexpr)[1]; } else { GPOS_ASSERT(fScalarCmp); CScalarCmp *popScCmp = CScalarCmp::PopConvert(pop); // Comparison semantics for stats purposes is looser // than regular comparison. (*pescmpt) = CStatsPredUtils::Estatscmpt(popScCmp->PmdidOp()); pexprLeft = (*pexpr)[0]; pexprRight = (*pexpr)[1]; } (*ppcrLeft) = CUtils::PcrExtractFromScIdOrCastScId(pexprLeft); (*ppcrRight) = CUtils::PcrExtractFromScIdOrCastScId(pexprRight); if (NULL == *ppcrLeft || NULL == *ppcrRight) { // failed to extract a scalar ident return false; } return true; }
//--------------------------------------------------------------------------- // @function: // CNormalizer::PushThruJoin // // @doc: // Push a conjunct through a join // // //--------------------------------------------------------------------------- void CNormalizer::PushThruJoin ( IMemoryPool *pmp, CExpression *pexprJoin, CExpression *pexprConj, CExpression **ppexprResult ) { GPOS_ASSERT(NULL != pexprConj); GPOS_ASSERT(NULL != ppexprResult); COperator *pop = pexprJoin->Pop(); const ULONG ulArity = pexprJoin->UlArity(); BOOL fLASApply = CUtils::FLeftAntiSemiApply(pop); COperator::EOperatorId eopid = pop->Eopid(); BOOL fOuterJoin = COperator::EopLogicalLeftOuterJoin == eopid || COperator::EopLogicalLeftOuterApply == eopid || COperator::EopLogicalLeftOuterCorrelatedApply == eopid; if (fOuterJoin && !CUtils::FScalarConstTrue(pexprConj)) { // whenever possible, push incoming predicate through outer join's outer child, // recursion will eventually reach the rest of PushThruJoin() to process join predicates PushThruOuterChild(pmp, pexprJoin, pexprConj, ppexprResult); return; } // combine conjunct with join predicate CExpression *pexprScalar = (*pexprJoin)[ulArity - 1]; CExpression *pexprPred = CPredicateUtils::PexprConjunction(pmp, pexprScalar, pexprConj); // break predicate to conjuncts DrgPexpr *pdrgpexprConjuncts = CPredicateUtils::PdrgpexprConjuncts(pmp, pexprPred); pexprPred->Release(); // push predicates through children and compute new child expressions DrgPexpr *pdrgpexprChildren = GPOS_NEW(pmp) DrgPexpr(pmp); for (ULONG ul = 0; ul < ulArity - 1; ul++) { CExpression *pexprChild = (*pexprJoin)[ul]; CExpression *pexprNewChild = NULL; if (fLASApply) { // do not push anti-semi-apply predicates to any of the children pexprNewChild = PexprNormalize(pmp, pexprChild); pdrgpexprChildren->Append(pexprNewChild); continue; } if (0 == ul && fOuterJoin) { // do not push outer join predicates through outer child // otherwise, we will throw away outer child's tuples that should // be part of the join result pexprNewChild = PexprNormalize(pmp, pexprChild); pdrgpexprChildren->Append(pexprNewChild); continue; } DrgPexpr *pdrgpexprRemaining = NULL; PushThru(pmp, pexprChild, pdrgpexprConjuncts, &pexprNewChild, &pdrgpexprRemaining); pdrgpexprChildren->Append(pexprNewChild); pdrgpexprConjuncts->Release(); pdrgpexprConjuncts = pdrgpexprRemaining; } // remaining conjuncts become the new join predicate CExpression *pexprNewScalar = CPredicateUtils::PexprConjunction(pmp, pdrgpexprConjuncts); pdrgpexprChildren->Append(pexprNewScalar); // create a new join expression pop->AddRef(); *ppexprResult = GPOS_NEW(pmp) CExpression(pmp, pop, pdrgpexprChildren); }
//--------------------------------------------------------------------------- // @function: // CNormalizer::PexprPullUpAndCombineProjects // // @doc: // Pulls up logical projects as far as possible, and combines consecutive // projects if possible // //--------------------------------------------------------------------------- CExpression * CNormalizer::PexprPullUpAndCombineProjects ( IMemoryPool *pmp, CExpression *pexpr, BOOL *pfSuccess // output to indicate whether anything was pulled up ) { GPOS_ASSERT(NULL != pexpr); GPOS_ASSERT(NULL != pfSuccess); COperator *pop = pexpr->Pop(); const ULONG ulArity = pexpr->UlArity(); if (!pop->FLogical() || 0 == ulArity) { pexpr->AddRef(); return pexpr; } DrgPexpr *pdrgpexprChildren = GPOS_NEW(pmp) DrgPexpr(pmp); DrgPexpr *pdrgpexprPrElPullUp = GPOS_NEW(pmp) DrgPexpr(pmp); CExpressionHandle exprhdl(pmp); exprhdl.Attach(pexpr); CColRefSet *pcrsOutput = CDrvdPropRelational::Pdprel(pexpr->PdpDerive())->PcrsOutput(); // extract the columns used by the scalar expression and the operator itself (for grouping, sorting, etc.) CColRefSet *pcrsUsed = exprhdl.PcrsUsedColumns(pmp); for (ULONG ul = 0; ul < ulArity; ul++) { CExpression *pexprChild = PexprPullUpAndCombineProjects(pmp, (*pexpr)[ul], pfSuccess); if (pop->FLogical() && CLogical::PopConvert(pop)->FCanPullProjectionsUp(ul) && COperator::EopLogicalProject == pexprChild->Pop()->Eopid()) { // this child is a project - see if any project elements can be pulled up CExpression *pexprNewChild = PexprPullUpProjectElements ( pmp, pexprChild, pcrsUsed, pcrsOutput, &pdrgpexprPrElPullUp ); pexprChild->Release(); pexprChild = pexprNewChild; } pdrgpexprChildren->Append(pexprChild); } pcrsUsed->Release(); pop->AddRef(); if (0 < pdrgpexprPrElPullUp->UlLength() && COperator::EopLogicalProject == pop->Eopid()) { // some project elements have been pulled up and the original expression // was a project - combine its project list with the pulled up project elements GPOS_ASSERT(2 == pdrgpexprChildren->UlLength()); *pfSuccess = true; CExpression *pexprRelational = (*pdrgpexprChildren)[0]; CExpression *pexprPrLOld = (*pdrgpexprChildren)[1]; pexprRelational->AddRef(); CUtils::AddRefAppend(pdrgpexprPrElPullUp, pexprPrLOld->PdrgPexpr()); pdrgpexprChildren->Release(); CExpression *pexprPrjList = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CScalarProjectList(pmp), pdrgpexprPrElPullUp); GPOS_ASSERT(CDrvdPropRelational::Pdprel(pexprRelational->PdpDerive())->PcrsOutput()->FSubset(CDrvdPropScalar::Pdpscalar(pexprPrjList->PdpDerive())->PcrsUsed())); return GPOS_NEW(pmp) CExpression(pmp, pop, pexprRelational, pexprPrjList); } CExpression *pexprOutput = GPOS_NEW(pmp) CExpression(pmp, pop, pdrgpexprChildren); if (0 == pdrgpexprPrElPullUp->UlLength()) { // no project elements were pulled up pdrgpexprPrElPullUp->Release(); return pexprOutput; } // some project elements were pulled - add a project on top of output expression *pfSuccess = true; CExpression *pexprPrjList = GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CScalarProjectList(pmp), pdrgpexprPrElPullUp); GPOS_ASSERT(CDrvdPropRelational::Pdprel(pexprOutput->PdpDerive())->PcrsOutput()->FSubset(CDrvdPropScalar::Pdpscalar(pexprPrjList->PdpDerive())->PcrsUsed())); return GPOS_NEW(pmp) CExpression(pmp, GPOS_NEW(pmp) CLogicalProject(pmp), pexprOutput, pexprPrjList); }