//---------------------------------------------------------------------------
//	@function:
//		CLogicalSequenceProject::Matches
//
//	@doc:
//		Matching function
//
//---------------------------------------------------------------------------
BOOL
CLogicalSequenceProject::Matches
	(
	COperator *pop
	)
	const
{
	GPOS_ASSERT(NULL != pop);
	if (Eopid() == pop->Eopid())
	{
		CLogicalSequenceProject *popLogicalSequenceProject = CLogicalSequenceProject::PopConvert(pop);
		return
			m_pds->Matches(popLogicalSequenceProject->Pds()) &&
			CWindowFrame::Equals(m_pdrgpwf, popLogicalSequenceProject->Pdrgpwf()) &&
			COrderSpec::Equals(m_pdrgpos, popLogicalSequenceProject->Pdrgpos());
	}

	return false;
}
//---------------------------------------------------------------------------
//	@function:
//		CXformSubqueryUnnest::PexprSubqueryUnnest
//
//	@doc:
//		Helper for unnesting subquery under a given context
//
//---------------------------------------------------------------------------
CExpression *
CXformSubqueryUnnest::PexprSubqueryUnnest
(
    IMemoryPool *pmp,
    CExpression *pexpr,
    BOOL fEnforceCorrelatedApply
)
{
    GPOS_ASSERT(NULL != pexpr);

    if (GPOS_FTRACE(EopttraceEnforceCorrelatedExecution) && !fEnforceCorrelatedApply)
    {
        // if correlated execution is enforced, we cannot generate an expression
        // that does not use correlated Apply
        return NULL;
    }

    // extract components
    CExpression *pexprOuter = (*pexpr)[0];
    CExpression *pexprScalar = (*pexpr)[1];

    // we add-ref the logical child since the resulting expression must re-use it
    pexprOuter->AddRef();

    CExpression *pexprNewOuter = NULL;
    CExpression *pexprResidualScalar = NULL;

    CSubqueryHandler::ESubqueryCtxt esqctxt = CSubqueryHandler::EsqctxtFilter;
    if (COperator::EopScalarProjectList == pexprScalar->Pop()->Eopid())
    {
        esqctxt = CSubqueryHandler::EsqctxtValue;
    }

    // calling the handler removes subqueries and sets new logical and scalar expressions
    CSubqueryHandler sh(pmp, fEnforceCorrelatedApply);
    if (!CSubqueryHandler::FProcess
            (
                sh,
                pexprOuter,
                pexprScalar,
                false /* fDisjunctionOrNegation */,
                esqctxt,
                &pexprNewOuter,
                &pexprResidualScalar
            )
       )
    {
        CRefCount::SafeRelease(pexprNewOuter);
        CRefCount::SafeRelease(pexprResidualScalar);

        return NULL;
    }

    // create a new alternative using the new logical and scalar expressions
    CExpression *pexprResult = NULL;
    if (CSubqueryHandler::EsqctxtFilter == esqctxt)
    {
        pexprResult = CUtils::PexprLogicalSelect(pmp, pexprNewOuter, pexprResidualScalar);
    }
    else
    {
        GPOS_ASSERT(CSubqueryHandler::EsqctxtValue == esqctxt);

        CLogicalSequenceProject *popSeqPrj = NULL;
        CLogicalGbAgg *popGbAgg = NULL;
        COperator::EOperatorId eopid = pexpr->Pop()->Eopid();

        switch (eopid)
        {
        case COperator::EopLogicalProject:
            pexprResult = CUtils::PexprLogicalProject(pmp, pexprNewOuter, pexprResidualScalar, false /*fNewComputedCol*/);
            break;

        case COperator::EopLogicalGbAgg:
            popGbAgg = CLogicalGbAgg::PopConvert(pexpr->Pop());
            popGbAgg->Pdrgpcr()->AddRef();
            pexprResult = CUtils::PexprLogicalGbAgg(pmp, popGbAgg->Pdrgpcr(), pexprNewOuter, pexprResidualScalar, popGbAgg->Egbaggtype());
            break;

        case COperator::EopLogicalSequenceProject:
            popSeqPrj = CLogicalSequenceProject::PopConvert(pexpr->Pop());
            popSeqPrj->Pds()->AddRef();
            popSeqPrj->Pdrgpos()->AddRef();
            popSeqPrj->Pdrgpwf()->AddRef();
            pexprResult =
                CUtils::PexprLogicalSequenceProject(pmp, popSeqPrj->Pds(), popSeqPrj->Pdrgpos(), popSeqPrj->Pdrgpwf(), pexprNewOuter, pexprResidualScalar);
            break;

        default:
            GPOS_ASSERT(!"Unnesting subqueries for an invalid operator");
            break;
        }
    }

    // normalize resulting expression
    CExpression *pexprNormalized = CNormalizer::PexprNormalize(pmp, pexprResult);
    pexprResult->Release();

    // pull up projections
    CExpression *pexprPullUpProjections = CNormalizer::PexprPullUpProjections(pmp, pexprNormalized);
    pexprNormalized->Release();

    return pexprPullUpProjections;
}