//--------------------------------------------------------------------------- // @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); }
//--------------------------------------------------------------------------- // @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]); } }
//--------------------------------------------------------------------------- // @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; }
//--------------------------------------------------------------------------- // @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; }
//--------------------------------------------------------------------------- // @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); }
//--------------------------------------------------------------------------- // @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; }
//--------------------------------------------------------------------------- // @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; }
//--------------------------------------------------------------------------- // @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); } }
//--------------------------------------------------------------------------- // @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); }