//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2009 Greenplum, Inc.
//
//	@filename:
//		CExpressionHandle.cpp
//
//	@doc:
//		Handle to an expression to abstract topology;
//
//		The handle provides access to an expression and the properties
//		of its children; regardless of whether the expression is a group
//		expression or a stand-alone tree;
//---------------------------------------------------------------------------

#include "gpopt/operators/CExpressionHandle.h"

#include "gpos/base.h"

#include "gpopt/base/CCTEReq.h"
#include "gpopt/base/CColRefSet.h"
#include "gpopt/base/CCostContext.h"
#include "gpopt/base/CDrvdPropCtxtPlan.h"
#include "gpopt/base/CDrvdPropScalar.h"
#include "gpopt/base/CKeyCollection.h"
#include "gpopt/base/COptCtxt.h"
#include "gpopt/base/CReqdPropPlan.h"
#include "gpopt/base/CUtils.h"
#include "gpopt/exception.h"
#include "gpopt/operators/CLogical.h"
#include "gpopt/operators/CLogicalCTEConsumer.h"
#include "gpopt/operators/CLogicalGbAgg.h"
#include "gpopt/operators/COperator.h"
#include "gpopt/operators/CPattern.h"
#include "gpopt/operators/CPhysicalCTEConsumer.h"
#include "gpopt/operators/CPhysicalScan.h"
#include "naucrates/statistics/CStatisticsUtils.h"

using namespace gpnaucrates;
using namespace gpopt;


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::CExpressionHandle
//
//	@doc:
//		ctor
//
//---------------------------------------------------------------------------
CExpressionHandle::CExpressionHandle(CMemoryPool *mp)
	: m_mp(mp),
	  m_pexpr(nullptr),
	  m_pgexpr(nullptr),
	  m_pcc(nullptr),
	  m_pdpplan(nullptr),
	  m_pstats(nullptr),
	  m_prp(nullptr),
	  m_pdrgpstat(nullptr),
	  m_pdrgprp(nullptr)
{
	GPOS_ASSERT(nullptr != mp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::~CExpressionHandle
//
//	@doc:
//		dtor
//
//		Since handles live on the stack this dtor will be called during
//		exceptions, hence, need to be defensive
//
//---------------------------------------------------------------------------
CExpressionHandle::~CExpressionHandle()
{
	CRefCount::SafeRelease(m_pexpr);
	CRefCount::SafeRelease(m_pgexpr);
	CRefCount::SafeRelease(m_pstats);
	CRefCount::SafeRelease(m_prp);
	CRefCount::SafeRelease(m_pdpplan);
	CRefCount::SafeRelease(m_pdrgpstat);
	CRefCount::SafeRelease(m_pdrgprp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FStatsDerived
//
//	@doc:
//		Check if stats are derived for attached expression and its children
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FStatsDerived() const
{
	IStatistics *stats = nullptr;
	if (nullptr != m_pexpr)
	{
		stats = const_cast<IStatistics *>(m_pexpr->Pstats());
	}
	else
	{
		GPOS_ASSERT(nullptr != m_pgexpr);
		stats = m_pgexpr->Pgroup()->Pstats();
	}

	if (nullptr == stats)
	{
		// stats of attached expression have not been derived yet
		return false;
	}

	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		if (FScalarChild(ul))
		{
			// skip scalar children
			continue;
		}

		IStatistics *child_stats = nullptr;
		if (nullptr != m_pexpr)
		{
			child_stats = const_cast<IStatistics *>((*m_pexpr)[ul]->Pstats());
		}
		else
		{
			child_stats = (*m_pgexpr)[ul]->Pstats();
		}

		if (nullptr == child_stats)
		{
			// stats of attached expression child have not been derived yet
			return false;
		}
	}

	return true;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::CopyStats
//
//	@doc:
//		Copy stats from attached expression/group expression to local stats
//		members
//
//---------------------------------------------------------------------------
void
CExpressionHandle::CopyStats()
{
	if (!FStatsDerived())
	{
		// stats of attached expression (or its children) have not been derived yet
		return;
	}

	IStatistics *stats = nullptr;
	if (nullptr != m_pexpr)
	{
		stats = const_cast<IStatistics *>(m_pexpr->Pstats());
	}
	else
	{
		GPOS_ASSERT(nullptr != m_pgexpr);
		stats = m_pgexpr->Pgroup()->Pstats();
	}
	GPOS_ASSERT(nullptr != stats);

	// attach stats
	stats->AddRef();
	GPOS_ASSERT(nullptr == m_pstats);
	m_pstats = stats;

	// attach child stats
	GPOS_ASSERT(nullptr == m_pdrgpstat);
	m_pdrgpstat = GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		IStatistics *child_stats = nullptr;
		if (nullptr != m_pexpr)
		{
			child_stats = const_cast<IStatistics *>((*m_pexpr)[ul]->Pstats());
		}
		else
		{
			child_stats = (*m_pgexpr)[ul]->Pstats();
		}

		if (nullptr != child_stats)
		{
			child_stats->AddRef();
		}
		else
		{
			GPOS_ASSERT(FScalarChild(ul));

			// create dummy stats for missing scalar children
			child_stats = CStatistics::MakeEmptyStats(m_mp);
		}

		m_pdrgpstat->Append(child_stats);
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Attach
//
//	@doc:
//		Attach to a given expression
//
//---------------------------------------------------------------------------
void
CExpressionHandle::Attach(CExpression *pexpr)
{
	GPOS_ASSERT(nullptr == m_pexpr);
	GPOS_ASSERT(nullptr == m_pgexpr);
	GPOS_ASSERT(nullptr != pexpr);

	// increment ref count on base expression
	pexpr->AddRef();
	m_pexpr = pexpr;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Attach
//
//	@doc:
//		Attach to a given group expression
//
//---------------------------------------------------------------------------
void
CExpressionHandle::Attach(CGroupExpression *pgexpr)
{
	GPOS_ASSERT(nullptr == m_pexpr);
	GPOS_ASSERT(nullptr == m_pgexpr);
	GPOS_ASSERT(nullptr != pgexpr);

	// increment ref count on group expression
	pgexpr->AddRef();
	m_pgexpr = pgexpr;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Attach
//
//	@doc:
//		Attach to a given cost context
//
//---------------------------------------------------------------------------
void
CExpressionHandle::Attach(CCostContext *pcc)
{
	GPOS_ASSERT(nullptr == m_pcc);
	GPOS_ASSERT(nullptr != pcc);

	m_pcc = pcc;
	Attach(pcc->Pgexpr());
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveProps
//
//	@doc:
//		Recursive property derivation
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveProps(CDrvdPropCtxt *pdpctxt)
{
	GPOS_CHECK_ABORT;

	if (nullptr != m_pgexpr)
	{
		return;
	}

	if (nullptr != m_pexpr->Pdp(m_pexpr->Ept()))
	{
		return;
	}

	// copy stats of attached expression
	CopyStats();

	m_pexpr->PdpDerive(pdpctxt);
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::PdrgpstatOuterRefs
//
//	@doc:
//		Given an array of stats objects and a child index, return an array
//		of stats objects starting from the first stats object referenced by
//		child
//
//---------------------------------------------------------------------------
IStatisticsArray *
CExpressionHandle::PdrgpstatOuterRefs(IStatisticsArray *statistics_array,
									  ULONG child_index)
{
	GPOS_ASSERT(nullptr != statistics_array);
	GPOS_ASSERT(child_index < Arity());

	if (FScalarChild(child_index) || !HasOuterRefs(child_index))
	{
		// if child is scalar or has no outer references, return empty array
		return GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	}

	IStatisticsArray *pdrgpstatResult = GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	CColRefSet *outer_refs = DeriveOuterReferences(child_index);
	GPOS_ASSERT(0 < outer_refs->Size());

	const ULONG size = statistics_array->Size();
	ULONG ulStartIndex = gpos::ulong_max;
	for (ULONG ul = 0; ul < size; ul++)
	{
		IStatistics *stats = (*statistics_array)[ul];
		CColRefSet *pcrsStats = stats->GetColRefSet(m_mp);
		BOOL fStatsColsUsed = !outer_refs->IsDisjoint(pcrsStats);
		pcrsStats->Release();
		if (fStatsColsUsed)
		{
			ulStartIndex = ul;
			break;
		}
	}

	if (gpos::ulong_max != ulStartIndex)
	{
		// copy stats starting from index of outer-most stats object referenced by child
		CUtils::AddRefAppend(pdrgpstatResult, statistics_array, ulStartIndex);
	}

	return pdrgpstatResult;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FAttachedToLeafPattern
//
//	@doc:
//		Return True if handle is attached to a leaf pattern
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FAttachedToLeafPattern() const
{
	return 0 == Arity() && nullptr != m_pexpr && nullptr != m_pexpr->Pgexpr();
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveRootStats
//
//	@doc:
//		CheckState derivation at root operator where handle is attached
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveRootStats(IStatisticsArray *stats_ctxt)
{
	GPOS_ASSERT(nullptr == m_pstats);

	CLogical *popLogical = CLogical::PopConvert(Pop());
	IStatistics *pstatsRoot = nullptr;
	if (FAttachedToLeafPattern())
	{
		// for leaf patterns extracted from memo, trigger state derivation on origin group
		GPOS_ASSERT(nullptr != m_pexpr);
		GPOS_ASSERT(nullptr != m_pexpr->Pgexpr());

		pstatsRoot = m_pexpr->Pgexpr()->Pgroup()->PstatsRecursiveDerive(
			m_mp, m_mp, CReqdPropRelational::GetReqdRelationalProps(m_prp),
			stats_ctxt);
		pstatsRoot->AddRef();
	}
	else
	{
		// otherwise, derive stats using root operator
		pstatsRoot = popLogical->PstatsDerive(m_mp, *this, stats_ctxt);
	}
	GPOS_ASSERT(nullptr != pstatsRoot);

	m_pstats = pstatsRoot;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveStats
//
//	@doc:
//		Recursive stat derivation
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveStats(IStatisticsArray *stats_ctxt,
							   BOOL fComputeRootStats)
{
	GPOS_ASSERT(nullptr != stats_ctxt);
	GPOS_ASSERT(nullptr == m_pdrgpstat);
	GPOS_ASSERT(nullptr == m_pstats);
	GPOS_ASSERT(nullptr != m_pdrgprp);

	// copy input context
	IStatisticsArray *pdrgpstatCurrentCtxt =
		GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	CUtils::AddRefAppend(pdrgpstatCurrentCtxt, stats_ctxt);

	// create array of children stats
	m_pdrgpstat = GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	ULONG ulMaxChildRisk = 1;
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		// create a new context for outer references used by current child
		IStatisticsArray *pdrgpstatChildCtxt =
			PdrgpstatOuterRefs(pdrgpstatCurrentCtxt, ul);

		IStatistics *stats = nullptr;
		if (nullptr != Pexpr())
		{
			// derive stats recursively on child expression
			stats = (*Pexpr())[ul]->PstatsDerive(GetReqdRelationalProps(ul),
												 pdrgpstatChildCtxt);
		}
		else
		{
			// derive stats recursively on child group
			stats = (*Pgexpr())[ul]->PstatsRecursiveDerive(
				m_mp, m_mp, GetReqdRelationalProps(ul), pdrgpstatChildCtxt);
		}
		GPOS_ASSERT(nullptr != stats);

		// add child stat to current context
		stats->AddRef();
		pdrgpstatCurrentCtxt->Append(stats);
		pdrgpstatChildCtxt->Release();

		// add child stat to children stat array
		stats->AddRef();
		m_pdrgpstat->Append(stats);
		if (stats->StatsEstimationRisk() > ulMaxChildRisk)
		{
			ulMaxChildRisk = stats->StatsEstimationRisk();
		}
	}

	if (fComputeRootStats)
	{
		// call stat derivation on operator to compute local stats
		GPOS_ASSERT(nullptr == m_pstats);

		DeriveRootStats(stats_ctxt);
		GPOS_ASSERT(nullptr != m_pstats);

		CLogical *popLogical = CLogical::PopConvert(Pop());
		ULONG risk = ulMaxChildRisk;
		if (CStatisticsUtils::IncreasesRisk(popLogical))
		{
			++risk;
		}
		m_pstats->SetStatsEstimationRisk(risk);
	}

	// clean up current stat context
	pdrgpstatCurrentCtxt->Release();
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveCostContextStats
//
//	@doc:
//		Stats derivation based on required plan properties
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveCostContextStats()
{
	GPOS_ASSERT(nullptr != m_pcc);
	GPOS_ASSERT(nullptr == m_pcc->Pstats());

	// copy group properties and stats
	CopyStats();

	if (nullptr != m_pstats && !m_pcc->FNeedsNewStats())
	{
		// there is no need to derive stats,
		// stats are copied from owner group
		// (note that m_pdrgpstat may not contain the correct DPE
		// stats of children, but it is used only for deriving the
		// stats, which we don't need to do anymore)

		return;
	}

	CEnfdPartitionPropagation *pepp = m_pcc->Poc()->Prpp()->Pepp();
	COperator *pop = Pop();
	if (CUtils::FPhysicalScan(pop) &&
		CPhysicalScan::PopConvert(pop)->FDynamicScan() &&
		pepp->PppsRequired()->ContainsAnyConsumers())
	{
		// derive stats on dynamic table scan using stats of part selector
		CPhysicalScan *popScan = CPhysicalScan::PopConvert(m_pgexpr->Pop());
		IStatistics *pstatsDS = popScan->PstatsDerive(
			m_mp, *this, m_pcc->Poc()->Prpp(), m_pcc->Poc()->Pdrgpstat());

		if (nullptr == m_pstats || m_pstats->Rows() > pstatsDS->Rows())
		{
			// Replace the group stats with our newly derived DPE stats
			CRefCount::SafeRelease(m_pstats);
			m_pstats = pstatsDS;
		}
		else
		{
			// Eliminating partitions can't possibly increase the row count. If the row
			// count is higher, that's likely due to some heuristics in the estimation.
			// If the row count is the same, there is no need to use DPE stats.
			pstatsDS->Release();
		}

		return;
	}

	// release current stats since we will derive new stats
	CRefCount::SafeRelease(m_pstats);
	m_pstats = nullptr;

	// load stats from child cost context(s) -- these may be different from child groups stats
	CRefCount::SafeRelease(m_pdrgpstat);
	m_pdrgpstat = nullptr;

	m_pdrgpstat = GPOS_NEW(m_mp) IStatisticsArray(m_mp);
	const ULONG arity = m_pcc->Pdrgpoc()->Size();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		COptimizationContext *pocChild = (*m_pcc->Pdrgpoc())[ul];
		CCostContext *pccChild = pocChild->PccBest();
		GPOS_ASSERT(nullptr != pccChild);
		GPOS_ASSERT(nullptr != pccChild->Pstats());

		pccChild->Pstats()->AddRef();
		m_pdrgpstat->Append(pccChild->Pstats());
	}

	if (CPhysical::PopConvert(m_pgexpr->Pop())->FPassThruStats())
	{
		GPOS_ASSERT(1 == m_pdrgpstat->Size());

		// copy stats from first child
		(*m_pdrgpstat)[0]->AddRef();
		m_pstats = (*m_pdrgpstat)[0];

		return;
	}

	// derive stats using the best logical expression with the same children as attached physical operator
	CGroupExpression *pgexprForStats = m_pcc->PgexprForStats();
	GPOS_ASSERT(nullptr != pgexprForStats);

	CExpressionHandle exprhdl(m_mp);
	exprhdl.Attach(pgexprForStats);
	exprhdl.DeriveProps(nullptr /*pdpctxt*/);
	m_pdrgpstat->AddRef();
	exprhdl.m_pdrgpstat = m_pdrgpstat;
	exprhdl.ComputeReqdProps(m_pcc->Poc()->GetReqdRelationalProps(),
							 0 /*ulOptReq*/);

	GPOS_ASSERT(nullptr == exprhdl.m_pstats);
	IStatistics *stats = m_pgexpr->Pgroup()->PstatsCompute(
		m_pcc->Poc(), exprhdl, pgexprForStats);

	// copy stats to main handle
	GPOS_ASSERT(nullptr == m_pstats);
	GPOS_ASSERT(nullptr != stats);

	stats->AddRef();
	m_pstats = stats;

	GPOS_ASSERT(m_pstats != nullptr);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveStats
//
//	@doc:
//		CheckState derivation using given properties and context
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveStats(CMemoryPool *pmpLocal, CMemoryPool *pmpGlobal,
							   CReqdPropRelational *prprel,
							   IStatisticsArray *stats_ctxt) const
{
	CReqdPropRelational *prprelNew = prprel;
	if (nullptr == prprelNew)
	{
		// create empty property container
		CColRefSet *pcrs = GPOS_NEW(pmpGlobal) CColRefSet(pmpGlobal);
		prprelNew = GPOS_NEW(pmpGlobal) CReqdPropRelational(pcrs);
	}
	else
	{
		prprelNew->AddRef();
	}

	IStatisticsArray *pdrgpstatCtxtNew = stats_ctxt;
	if (nullptr == stats_ctxt)
	{
		// create empty context
		pdrgpstatCtxtNew = GPOS_NEW(pmpGlobal) IStatisticsArray(pmpGlobal);
	}
	else
	{
		pdrgpstatCtxtNew->AddRef();
	}

	if (nullptr != Pgexpr())
	{
		(void) Pgexpr()->Pgroup()->PstatsRecursiveDerive(
			pmpLocal, pmpGlobal, prprelNew, pdrgpstatCtxtNew);
	}
	else
	{
		GPOS_ASSERT(nullptr != Pexpr());

		(void) Pexpr()->PstatsDerive(prprelNew, pdrgpstatCtxtNew);
	}

	prprelNew->Release();
	pdrgpstatCtxtNew->Release();
}


// Derive the properties of the plan carried by attached cost context.
// Note that this re-derives the plan properties, instead of using those
// present in the gexpr, for cost contexts only and under the default
// CDrvdPropCtxtPlan.
// On the other hand, the properties in the gexpr may have been derived in
// other non-default contexts (e.g with cte info).
void
CExpressionHandle::DerivePlanPropsForCostContext()
{
	GPOS_ASSERT(nullptr != m_pcc);
	GPOS_ASSERT(nullptr != m_pgexpr);
	GPOS_CHECK_ABORT;

	CDrvdPropCtxtPlan *pdpctxtplan = GPOS_NEW(m_mp) CDrvdPropCtxtPlan(m_mp);
	CopyStats();


	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 (nullptr != pdpplan)
		{
			pdpctxtplan->CopyCTEProducerProps(pdpplan, ulCTEId);
		}
	}

	// create/derive local properties
	m_pdpplan = Pop()->PdpCreate(m_mp);
	m_pdpplan->Derive(m_mp, *this, pdpctxtplan);
	pdpctxtplan->Release();
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::InitReqdProps
//
//	@doc:
//		Init required properties containers
//
//
//---------------------------------------------------------------------------
void
CExpressionHandle::InitReqdProps(CReqdProp *prpInput)
{
	GPOS_ASSERT(nullptr != prpInput);
	GPOS_ASSERT(nullptr == m_prp);
	GPOS_ASSERT(nullptr == m_pdrgprp);

	// set required properties of attached expr/gexpr
	m_prp = prpInput;
	m_prp->AddRef();

	// compute required properties of children
	m_pdrgprp = GPOS_NEW(m_mp) CReqdPropArray(m_mp);

	// initialize array with input requirements,
	// the initial requirements are only place holders in the array
	// and they are replaced when computing the requirements of each child
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		m_prp->AddRef();
		m_pdrgprp->Append(m_prp);
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::ComputeChildReqdProps
//
//	@doc:
//		Compute required properties of the n-th child
//
//
//---------------------------------------------------------------------------
void
CExpressionHandle::ComputeChildReqdProps(ULONG child_index,
										 CDrvdPropArray *pdrgpdpCtxt,
										 ULONG ulOptReq)
{
	GPOS_ASSERT(nullptr != m_prp);
	GPOS_ASSERT(nullptr != m_pdrgprp);
	GPOS_ASSERT(m_pdrgprp->Size() == Arity());
	GPOS_ASSERT(child_index < m_pdrgprp->Size() &&
				"uninitialized required child properties");
	GPOS_CHECK_ABORT;

	CReqdProp *prp = m_prp;
	if (FScalarChild(child_index))
	{
		// use local reqd properties to fill scalar child entry in children array
		prp->AddRef();
	}
	else
	{
		// compute required properties based on child type
		prp = Pop()->PrpCreate(m_mp);
		prp->Compute(m_mp, *this, m_prp, child_index, pdrgpdpCtxt, ulOptReq);
	}

	// replace required properties of given child
	m_pdrgprp->Replace(child_index, prp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::CopyChildReqdProps
//
//	@doc:
//		Copy required properties of the n-th child
//
//
//---------------------------------------------------------------------------
void
CExpressionHandle::CopyChildReqdProps(ULONG child_index, CReqdProp *prp)
{
	GPOS_ASSERT(nullptr != prp);
	GPOS_ASSERT(nullptr != m_pdrgprp);
	GPOS_ASSERT(m_pdrgprp->Size() == Arity());
	GPOS_ASSERT(child_index < m_pdrgprp->Size() &&
				"uninitialized required child properties");

	m_pdrgprp->Replace(child_index, prp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::ComputeChildReqdCols
//
//	@doc:
//		Compute required columns of the n-th child
//
//
//---------------------------------------------------------------------------
void
CExpressionHandle::ComputeChildReqdCols(ULONG child_index,
										CDrvdPropArray *pdrgpdpCtxt)
{
	GPOS_ASSERT(nullptr != m_prp);
	GPOS_ASSERT(nullptr != m_pdrgprp);
	GPOS_ASSERT(m_pdrgprp->Size() == Arity());
	GPOS_ASSERT(child_index < m_pdrgprp->Size() &&
				"uninitialized required child properties");

	CReqdProp *prp = m_prp;
	if (FScalarChild(child_index))
	{
		// use local reqd properties to fill scalar child entry in children array
		prp->AddRef();
	}
	else
	{
		// compute required columns
		prp = Pop()->PrpCreate(m_mp);
		CReqdPropPlan::Prpp(prp)->ComputeReqdCols(m_mp, *this, m_prp,
												  child_index, pdrgpdpCtxt);
	}

	// replace required properties of given child
	m_pdrgprp->Replace(child_index, prp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::ComputeReqdProps
//
//	@doc:
//		Set required properties of attached expr/gexpr, and compute required
//		properties of all children
//
//---------------------------------------------------------------------------
void
CExpressionHandle::ComputeReqdProps(CReqdProp *prpInput, ULONG ulOptReq)
{
	InitReqdProps(prpInput);
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		ComputeChildReqdProps(ul, nullptr /*pdrgpdpCtxt*/, ulOptReq);
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FScalarChild
//
//	@doc:
//		Check if a given child is a scalar expression/group
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FScalarChild(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->Pop()->FScalar();
	}

	GPOS_ASSERT(nullptr != Pgexpr());

	return (*Pgexpr())[child_index]->FScalar();
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Arity
//
//	@doc:
//		Return number of children of attached expression/group expression
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::Arity() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->Arity();
	}

	GPOS_ASSERT(nullptr != Pgexpr());

	return Pgexpr()->Arity();
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlLastNonScalarChild
//
//	@doc:
//		Return the index of the last non-scalar child. This is only valid if
//		Arity() is greater than 0
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlLastNonScalarChild() const
{
	const ULONG arity = Arity();
	if (0 == arity)
	{
		return gpos::ulong_max;
	}

	ULONG ulLastNonScalarChild = arity - 1;
	while (0 < ulLastNonScalarChild && FScalarChild(ulLastNonScalarChild))
	{
		ulLastNonScalarChild--;
	}

	if (!FScalarChild(ulLastNonScalarChild))
	{
		// we need to check again that index points to a non-scalar child
		// since operator's children may be all scalar (e.g. index-scan)
		return ulLastNonScalarChild;
	}

	return gpos::ulong_max;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlFirstNonScalarChild
//
//	@doc:
//		Return the index of the first non-scalar child. This is only valid if
//		Arity() is greater than 0
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlFirstNonScalarChild() const
{
	const ULONG arity = Arity();
	if (0 == arity)
	{
		return gpos::ulong_max;
	}

	ULONG ulFirstNonScalarChild = 0;
	while (ulFirstNonScalarChild < arity - 1 &&
		   FScalarChild(ulFirstNonScalarChild))
	{
		ulFirstNonScalarChild++;
	}

	if (!FScalarChild(ulFirstNonScalarChild))
	{
		// we need to check again that index points to a non-scalar child
		// since operator's children may be all scalar (e.g. index-scan)
		return ulFirstNonScalarChild;
	}

	return gpos::ulong_max;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlNonScalarChildren
//
//	@doc:
//		Return number of non-scalar children
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlNonScalarChildren() const
{
	const ULONG arity = Arity();
	ULONG ulNonScalarChildren = 0;
	for (ULONG ul = 0; ul < arity; ul++)
	{
		if (!FScalarChild(ul))
		{
			ulNonScalarChildren++;
		}
	}

	return ulNonScalarChildren;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::GetRelationalProperties
//
//	@doc:
//		Retrieve derived relational props of n-th child;
//		Assumes caller knows what properties to ask for;
//
//---------------------------------------------------------------------------
CDrvdPropRelational *
CExpressionHandle::GetRelationalProperties(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		// handle is used for required property computation
		if (Pexpr()->Pop()->FPhysical())
		{
			// relational props were copied from memo, return props directly
			return (*Pexpr())[child_index]->GetDrvdPropRelational();
		}

		// return props after calling derivation function
		return CDrvdPropRelational::GetRelationalProperties(
			(*Pexpr())[child_index]->PdpDerive());
	}

	GPOS_ASSERT(nullptr != m_pcc || nullptr != m_pgexpr);

	// handle is used for deriving plan properties, get relational props from child group
	CDrvdPropRelational *drvdProps =
		CDrvdPropRelational::GetRelationalProperties(
			(*Pgexpr())[child_index]->Pdp());
	GPOS_ASSERT(drvdProps->IsComplete());

	return drvdProps;
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::GetRelationalProperties
//
//	@doc:
//		Retrieve relational properties of attached expr/gexpr;
//
//---------------------------------------------------------------------------
CDrvdPropRelational *
CExpressionHandle::GetRelationalProperties() const
{
	if (nullptr != Pexpr())
	{
		if (Pexpr()->Pop()->FPhysical())
		{
			// relational props were copied from memo, return props directly
			CDrvdPropRelational *drvdProps = Pexpr()->GetDrvdPropRelational();
			GPOS_ASSERT(drvdProps->IsComplete());
			return drvdProps;
		}
		// return props after calling derivation function
		return CDrvdPropRelational::GetRelationalProperties(
			Pexpr()->PdpDerive());
	}

	GPOS_ASSERT(nullptr != m_pcc || nullptr != m_pgexpr);

	// get relational props from group
	CDrvdPropRelational *drvdProps =
		CDrvdPropRelational::GetRelationalProperties(Pgexpr()->Pgroup()->Pdp());
	GPOS_ASSERT(drvdProps->IsComplete());

	return drvdProps;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Pstats
//
//	@doc:
//		Return derived stats of n-th child
//
//---------------------------------------------------------------------------
IStatistics *
CExpressionHandle::Pstats(ULONG child_index) const
{
	GPOS_ASSERT(child_index < m_pdrgpstat->Size());

	return (*m_pdrgpstat)[child_index];
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Pdpplan
//
//	@doc:
//		Retrieve derived plan props of n-th child;
//		Assumes caller knows what properties to ask for;
//
//---------------------------------------------------------------------------
CDrvdPropPlan *
CExpressionHandle::Pdpplan(ULONG child_index) const
{
	if (nullptr != m_pexpr)
	{
		return CDrvdPropPlan::Pdpplan(
			(*m_pexpr)[child_index]->Pdp(CDrvdProp::EptPlan));
	}

	GPOS_ASSERT(nullptr != m_pcc || nullptr != m_pgexpr);

	COptimizationContext *pocChild = (*m_pcc->Pdrgpoc())[child_index];
	CDrvdPropPlan *pdpplan = pocChild->PccBest()->Pdpplan();

	return pdpplan;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::GetDrvdScalarProps
//
//	@doc:
//		Retrieve derived scalar props of n-th child;
//		Assumes caller knows what properties to ask for;
//
//---------------------------------------------------------------------------
CDrvdPropScalar *
CExpressionHandle::GetDrvdScalarProps(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		// handle is used for required property computation
		return CDrvdPropScalar::GetDrvdScalarProps(
			(*Pexpr())[child_index]->PdpDerive());
	}

	GPOS_ASSERT(nullptr != m_pcc || nullptr != m_pgexpr);

	// handle is used for deriving plan properties, get scalar props from child group
	return CDrvdPropScalar::GetDrvdScalarProps((*Pgexpr())[child_index]->Pdp());
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::GetReqdRelationalProps
//
//	@doc:
//		Retrieve required relational props of n-th child;
//		Assumes caller knows what properties to ask for;
//
//---------------------------------------------------------------------------
CReqdPropRelational *
CExpressionHandle::GetReqdRelationalProps(ULONG child_index) const
{
	GPOS_ASSERT(child_index < m_pdrgprp->Size());

	CReqdProp *prp = (*m_pdrgprp)[child_index];
	GPOS_ASSERT(prp->FRelational() && "Unexpected property type");

	return CReqdPropRelational::GetReqdRelationalProps(prp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Prpp
//
//	@doc:
//		Retrieve required relational props of n-th child;
//		Assumes caller knows what properties to ask for;
//
//---------------------------------------------------------------------------
CReqdPropPlan *
CExpressionHandle::Prpp(ULONG child_index) const
{
	GPOS_ASSERT(child_index < m_pdrgprp->Size());

	CReqdProp *prp = (*m_pdrgprp)[child_index];
	GPOS_ASSERT(prp->FPlan() && "Unexpected property type");

	return CReqdPropPlan::Prpp(prp);
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Pop
//
//	@doc:
//		Get operator from handle
//
//---------------------------------------------------------------------------
COperator *
CExpressionHandle::Pop() const
{
	if (nullptr != m_pexpr)
	{
		GPOS_ASSERT(nullptr == m_pgexpr);

		return m_pexpr->Pop();
	}

	if (nullptr != m_pgexpr)
	{
		return m_pgexpr->Pop();
	}

	GPOS_ASSERT(!"Handle was not attached properly");
	return nullptr;
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::Pop
//
//	@doc:
//		Get child operator from handle
//
//---------------------------------------------------------------------------
COperator *
CExpressionHandle::Pop(ULONG child_index) const
{
	GPOS_ASSERT(child_index < Arity());

	if (nullptr != m_pexpr)
	{
		GPOS_ASSERT(nullptr == m_pgexpr);

		return (*m_pexpr)[child_index]->Pop();
	}

	if (nullptr != m_pcc)
	{
		COptimizationContext *pocChild = (*m_pcc->Pdrgpoc())[child_index];
		GPOS_ASSERT(nullptr != pocChild);

		CCostContext *pccChild = pocChild->PccBest();
		GPOS_ASSERT(nullptr != pccChild);

		return pccChild->Pgexpr()->Pop();
	}

	return nullptr;
}

COperator *
CExpressionHandle::PopGrandchild(ULONG child_index, ULONG grandchild_index,
								 CCostContext **grandchildContext) const
{
	GPOS_ASSERT(child_index < Arity());

	if (grandchildContext)
	{
		*grandchildContext = nullptr;
	}

	if (nullptr != m_pexpr)
	{
		GPOS_ASSERT(nullptr == m_pcc);

		CExpression *childExpr = (*m_pexpr)[child_index];

		if (nullptr != childExpr)
		{
			return (*childExpr)[grandchild_index]->Pop();
		}

		return nullptr;
	}

	if (nullptr != m_pcc)
	{
		COptimizationContext *pocChild = (*m_pcc->Pdrgpoc())[child_index];
		GPOS_ASSERT(nullptr != pocChild);

		CCostContext *pccChild = pocChild->PccBest();
		GPOS_ASSERT(nullptr != pccChild);

		COptimizationContext *pocGrandchild =
			(*pccChild->Pdrgpoc())[grandchild_index];

		if (nullptr != pocGrandchild)
		{
			CCostContext *pccgrandchild = pocGrandchild->PccBest();

			if (grandchildContext)
			{
				*grandchildContext = pccgrandchild;
			}

			return pccgrandchild->Pgexpr()->Pop();
		}
	}

	return nullptr;
}



//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::DeriveProducerStats
//
//	@doc:
//		If the child (child_index) is a CTE consumer, then derive is corresponding
//		producer statistics.
//
//---------------------------------------------------------------------------
void
CExpressionHandle::DeriveProducerStats(ULONG child_index,
									   CColRefSet *pcrsStats) const
{
	// check to see if there are any CTE consumers in the group whose properties have
	// to be pushed to its corresponding CTE producer
	CGroupExpression *pgexpr = Pgexpr();
	if (nullptr != pgexpr)
	{
		CGroup *pgroupChild = (*pgexpr)[child_index];
		if (pgroupChild->FHasAnyCTEConsumer())
		{
			CGroupExpression *pgexprCTEConsumer =
				pgroupChild->PgexprAnyCTEConsumer();
			CLogicalCTEConsumer *popConsumer =
				CLogicalCTEConsumer::PopConvert(pgexprCTEConsumer->Pop());
			COptCtxt::PoctxtFromTLS()->Pcteinfo()->DeriveProducerStats(
				popConsumer, pcrsStats);
		}

		return;
	}

	// statistics are also derived on expressions representing the producer that may have
	// multiple CTE consumers. We should ensure that their properties are to pushed to their
	// corresponding CTE producer
	CExpression *pexpr = Pexpr();
	if (nullptr != pexpr)
	{
		CExpression *pexprChild = (*pexpr)[child_index];
		if (COperator::EopLogicalCTEConsumer == pexprChild->Pop()->Eopid())
		{
			CLogicalCTEConsumer *popConsumer =
				CLogicalCTEConsumer::PopConvert(pexprChild->Pop());
			COptCtxt::PoctxtFromTLS()->Pcteinfo()->DeriveProducerStats(
				popConsumer, pcrsStats);
		}
	}
}

//---------------------------------------------------------------------------
// CExpressionHandle::PexprScalarRepChild
//
// Get a representative (inexact) scalar child at given index. Subqueries
// in the child are replaced by a TRUE or NULL constant. Use this method
// where exactness is not required, e. g. for statistics derivation,
// costing, or for heuristics.
//
//---------------------------------------------------------------------------
CExpression *
CExpressionHandle::PexprScalarRepChild(ULONG child_index) const
{
	GPOS_ASSERT(child_index < Arity());

	if (nullptr != m_pgexpr)
	{
		// access scalar expression cached on the child scalar group
		GPOS_ASSERT((*m_pgexpr)[child_index]->FScalar());

		CExpression *pexprScalar = (*m_pgexpr)[child_index]->PexprScalarRep();
		GPOS_ASSERT(nullptr != pexprScalar);

		return pexprScalar;
	}

	if (nullptr != m_pexpr && nullptr != (*m_pexpr)[child_index]->Pgexpr())
	{
		// if the expression does not come from a group, but its child does then
		// get the scalar child from that group
		CGroupExpression *pgexpr = (*m_pexpr)[child_index]->Pgexpr();
		CExpression *pexprScalar = pgexpr->Pgroup()->PexprScalarRep();
		GPOS_ASSERT(nullptr != pexprScalar);

		return pexprScalar;
	}

	// access scalar expression from the child expression node
	GPOS_ASSERT((*m_pexpr)[child_index]->Pop()->FScalar());

	return (*m_pexpr)[child_index];
}


//---------------------------------------------------------------------------
// CExpressionHandle::PexprScalarRep
//
// Get a representative scalar expression attached to handle,
// return NULL if handle is not attached to a scalar expression.
// Note that this may be inexact if handle is attached to a
// CGroupExpression - subqueries will be replaced by a TRUE or NULL
// constant. Use this method where exactness is not required, e. g.
// for statistics derivation, costing, or for heuristics.
//
//---------------------------------------------------------------------------
CExpression *
CExpressionHandle::PexprScalarRep() const
{
	if (!Pop()->FScalar())
	{
		return nullptr;
	}

	if (nullptr != m_pexpr)
	{
		return m_pexpr;
	}

	if (nullptr != m_pgexpr)
	{
		return m_pgexpr->Pgroup()->PexprScalarRep();
	}

	return nullptr;
}


// return an exact scalar child at given index or return null if not possible
// (use this where exactness is required, e.g. for constraint derivation)
CExpression *
CExpressionHandle::PexprScalarExactChild(ULONG child_index,
										 BOOL error_on_null_return) const
{
	CExpression *result_expr = nullptr;
	if (nullptr != m_pgexpr && !(*m_pgexpr)[child_index]->FScalarRepIsExact())
	{
		result_expr = nullptr;
	}

	else if (nullptr != m_pexpr &&
			 nullptr != (*m_pexpr)[child_index]->Pgexpr() &&
			 !((*m_pexpr)[child_index]
				   ->Pgexpr()
				   ->Pgroup()
				   ->FScalarRepIsExact()))
	{
		// the expression does not come from a group, but its child does and
		// the child group does not have an exact expression
		result_expr = nullptr;
	}

	else
	{
		result_expr = PexprScalarRepChild(child_index);
	}
	if (nullptr == result_expr && error_on_null_return)
	{
		GPOS_RAISE(CException::ExmaInvalid, CException::ExmiInvalid,
				   GPOS_WSZ_LIT("Generated invalid plan with subquery"));
	}
	return result_expr;
}

// return an exact scalar expression attached to handle or null if not possible
// (use this where exactness is required, e.g. for constraint derivation)
CExpression *
CExpressionHandle::PexprScalarExact() const
{
	if (nullptr != m_pgexpr && !m_pgexpr->Pgroup()->FScalarRepIsExact())
	{
		return nullptr;
	}

	return PexprScalarRep();
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::PfpChild
//
//	@doc:
//		Retrieve derived function props of n-th child;
//
//---------------------------------------------------------------------------
CFunctionProp *
CExpressionHandle::PfpChild(ULONG child_index) const
{
	if (FScalarChild(child_index))
	{
		return DeriveScalarFunctionProperties(child_index);
	}

	return this->DeriveFunctionProperties(child_index);
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FChildrenHaveVolatileFuncScan
//
//	@doc:
//		Check whether an expression's children have a volatile function scan
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FChildrenHaveVolatileFuncScan() const
{
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		if (PfpChild(ul)->FHasVolatileFunctionScan())
		{
			return true;
		}
	}

	return false;
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FChildrenHaveVolatileFunc
//
//	@doc:
//		Check whether an expression's children have a volatile function
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FChildrenHaveVolatileFunc() const
{
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		if (PfpChild(ul)->Efs() == IMDFunction::EfsVolatile)
		{
			return true;
		}
	}

	return false;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlFirstOptimizedChildIndex
//
//	@doc:
//		Return the index of first child to be optimized
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlFirstOptimizedChildIndex() const
{
	const ULONG arity = Arity();
	GPOS_ASSERT(0 < arity);

	CPhysical::EChildExecOrder eceo = CPhysical::PopConvert(Pop())->Eceo();
	if (CPhysical::EceoRightToLeft == eceo)
	{
		return arity - 1;
	}
	GPOS_ASSERT(CPhysical::EceoLeftToRight == eceo);

	return 0;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlLastOptimizedChildIndex
//
//	@doc:
//		Return the index of last child to be optimized
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlLastOptimizedChildIndex() const
{
	const ULONG arity = Arity();
	GPOS_ASSERT(0 < arity);

	CPhysical::EChildExecOrder eceo = CPhysical::PopConvert(Pop())->Eceo();
	if (CPhysical::EceoRightToLeft == eceo)
	{
		return 0;
	}
	GPOS_ASSERT(CPhysical::EceoLeftToRight == eceo);

	return arity - 1;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlNextOptimizedChildIndex
//
//	@doc:
//		Return the index of child to be optimized next to the given child,
//		return gpos::ulong_max if there is no next child index
//
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlNextOptimizedChildIndex(ULONG child_index) const
{
	CPhysical::EChildExecOrder eceo = CPhysical::PopConvert(Pop())->Eceo();

	ULONG ulNextChildIndex = gpos::ulong_max;
	if (CPhysical::EceoRightToLeft == eceo)
	{
		if (0 < child_index)
		{
			ulNextChildIndex = child_index - 1;
		}
	}
	else
	{
		GPOS_ASSERT(CPhysical::EceoLeftToRight == eceo);

		if (Arity() - 1 > child_index)
		{
			ulNextChildIndex = child_index + 1;
		}
	}

	return ulNextChildIndex;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::UlPreviousOptimizedChildIndex
//
//	@doc:
//		Return the index of child optimized before the given child,
//		return gpos::ulong_max if there is no previous child index
//
//
//---------------------------------------------------------------------------
ULONG
CExpressionHandle::UlPreviousOptimizedChildIndex(ULONG child_index) const
{
	CPhysical::EChildExecOrder eceo = CPhysical::PopConvert(Pop())->Eceo();

	ULONG ulPrevChildIndex = gpos::ulong_max;
	if (CPhysical::EceoRightToLeft == eceo)
	{
		if (Arity() - 1 > child_index)
		{
			ulPrevChildIndex = child_index + 1;
		}
	}
	else
	{
		GPOS_ASSERT(CPhysical::EceoLeftToRight == eceo);

		if (0 < child_index)
		{
			ulPrevChildIndex = child_index - 1;
		}
	}

	return ulPrevChildIndex;
}


//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::FNextChildIndex
//
//	@doc:
//		Get next child index based on child optimization order, return
//		true if such index could be found
//
//---------------------------------------------------------------------------
BOOL
CExpressionHandle::FNextChildIndex(ULONG *pulChildIndex) const
{
	GPOS_ASSERT(nullptr != pulChildIndex);

	const ULONG arity = Arity();
	if (0 == arity)
	{
		// operator does not have children
		return false;
	}

	ULONG ulNextChildIndex = UlNextOptimizedChildIndex(*pulChildIndex);
	if (gpos::ulong_max == ulNextChildIndex)
	{
		return false;
	}
	*pulChildIndex = ulNextChildIndex;

	return true;
}

//---------------------------------------------------------------------------
//	@function:
//		CExpressionHandle::PcrsUsedColumns
//
//	@doc:
//		Return the columns used by a logical operator and all its scalar children
//
//---------------------------------------------------------------------------
CColRefSet *
CExpressionHandle::PcrsUsedColumns(CMemoryPool *mp) const
{
	COperator *pop = Pop();
	GPOS_ASSERT(pop->FLogical());

	CColRefSet *pcrs = GPOS_NEW(mp) CColRefSet(mp);

	// get columns used by the operator itself
	pcrs->Include(CLogical::PopConvert(pop)->PcrsLocalUsed());

	// get columns used by the scalar children
	const ULONG arity = Arity();
	for (ULONG ul = 0; ul < arity; ul++)
	{
		if (FScalarChild(ul))
		{
			pcrs->Include(DeriveUsedColumns(ul));
		}
	}

	return pcrs;
}

CDrvdProp *
CExpressionHandle::Pdp() const
{
	if (nullptr != m_pcc)
	{
		GPOS_ASSERT(m_pdpplan != nullptr);
		return m_pdpplan;
	}

	if (nullptr != Pexpr())
	{
		return Pexpr()->Pdp(Pexpr()->Ept());
	}

	GPOS_ASSERT(nullptr != Pgexpr());
	return Pgexpr()->Pgroup()->Pdp();
}

IStatistics *
CExpressionHandle::Pstats()
{
	return m_pstats;
}

// The below functions use on-demand property derivation
// only if there is an expression associated with the expression handle.
// If there is only a group expression or a cost context assoicated with the handle,
// all properties must have already been derived as we can't derive anything.
CColRefSet *
CExpressionHandle::DeriveOuterReferences(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveOuterReferences();
	}

	return GetRelationalProperties(child_index)->GetOuterReferences();
}

CColRefSet *
CExpressionHandle::DeriveOuterReferences() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveOuterReferences();
	}

	return GetRelationalProperties()->GetOuterReferences();
}

CColRefSet *
CExpressionHandle::DeriveOutputColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveOutputColumns();
	}

	return GetRelationalProperties(child_index)->GetOutputColumns();
}

CColRefSet *
CExpressionHandle::DeriveOutputColumns() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveOutputColumns();
	}

	return GetRelationalProperties()->GetOutputColumns();
}

CColRefSet *
CExpressionHandle::DeriveNotNullColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveNotNullColumns();
	}

	return GetRelationalProperties(child_index)->GetNotNullColumns();
}

CColRefSet *
CExpressionHandle::DeriveNotNullColumns() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveNotNullColumns();
	}

	return GetRelationalProperties()->GetNotNullColumns();
}

CMaxCard
CExpressionHandle::DeriveMaxCard(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveMaxCard();
	}

	return GetRelationalProperties(child_index)->GetMaxCard();
}

CMaxCard
CExpressionHandle::DeriveMaxCard() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveMaxCard();
	}

	return GetRelationalProperties()->GetMaxCard();
}

CColRefSet *
CExpressionHandle::DeriveCorrelatedApplyColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveCorrelatedApplyColumns();
	}

	return GetRelationalProperties(child_index)->GetCorrelatedApplyColumns();
}

CColRefSet *
CExpressionHandle::DeriveCorrelatedApplyColumns() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveCorrelatedApplyColumns();
	}

	return GetRelationalProperties()->GetCorrelatedApplyColumns();
}

CKeyCollection *
CExpressionHandle::DeriveKeyCollection(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveKeyCollection();
	}

	return GetRelationalProperties(child_index)->GetKeyCollection();
}

CKeyCollection *
CExpressionHandle::DeriveKeyCollection() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveKeyCollection();
	}

	return GetRelationalProperties()->GetKeyCollection();
}

CPropConstraint *
CExpressionHandle::DerivePropertyConstraint(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DerivePropertyConstraint();
	}

	return GetRelationalProperties(child_index)->GetPropertyConstraint();
}

CPropConstraint *
CExpressionHandle::DerivePropertyConstraint() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DerivePropertyConstraint();
	}

	return GetRelationalProperties()->GetPropertyConstraint();
}

ULONG
CExpressionHandle::DeriveJoinDepth(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveJoinDepth();
	}

	return GetRelationalProperties(child_index)->GetJoinDepth();
}

ULONG
CExpressionHandle::DeriveJoinDepth() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveJoinDepth();
	}

	return GetRelationalProperties()->GetJoinDepth();
}

CFunctionProp *
CExpressionHandle::DeriveFunctionProperties(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveFunctionProperties();
	}

	return GetRelationalProperties(child_index)->GetFunctionProperties();
}

CFunctionProp *
CExpressionHandle::DeriveFunctionProperties() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveFunctionProperties();
	}

	return GetRelationalProperties()->GetFunctionProperties();
}

CFunctionalDependencyArray *
CExpressionHandle::Pdrgpfd(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveFunctionalDependencies();
	}

	return GetRelationalProperties(child_index)->GetFunctionalDependencies();
}

CFunctionalDependencyArray *
CExpressionHandle::Pdrgpfd() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveFunctionalDependencies();
	}

	return GetRelationalProperties()->GetFunctionalDependencies();
}

CPartInfo *
CExpressionHandle::DerivePartitionInfo(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DerivePartitionInfo();
	}

	return GetRelationalProperties(child_index)->GetPartitionInfo();
}

CPartInfo *
CExpressionHandle::DerivePartitionInfo() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DerivePartitionInfo();
	}

	return GetRelationalProperties()->GetPartitionInfo();
}

CTableDescriptorHashSet *
CExpressionHandle::DeriveTableDescriptor() const
{
	if (nullptr != Pexpr())
	{
		return Pexpr()->DeriveTableDescriptor();
	}

	return GetRelationalProperties()->GetTableDescriptor();
}

CTableDescriptorHashSet *
CExpressionHandle::DeriveTableDescriptor(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveTableDescriptor();
	}

	return GetRelationalProperties(child_index)->GetTableDescriptor();
}
// Scalar property accessors

CColRefSet *
CExpressionHandle::DeriveDefinedColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveDefinedColumns();
	}

	return GetDrvdScalarProps(child_index)->GetDefinedColumns();
}

CColRefSet *
CExpressionHandle::DeriveUsedColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveUsedColumns();
	}

	return GetDrvdScalarProps(child_index)->GetUsedColumns();
}

CColRefSet *
CExpressionHandle::DeriveSetReturningFunctionColumns(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveSetReturningFunctionColumns();
	}

	return GetDrvdScalarProps(child_index)->GetSetReturningFunctionColumns();
}

BOOL
CExpressionHandle::DeriveHasSubquery(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveHasSubquery();
	}

	return GetDrvdScalarProps(child_index)->HasSubquery();
}

CPartInfo *
CExpressionHandle::DeriveScalarPartitionInfo(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveScalarPartitionInfo();
	}

	return GetDrvdScalarProps(child_index)->GetPartitionInfo();
}

CFunctionProp *
CExpressionHandle::DeriveScalarFunctionProperties(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveScalarFunctionProperties();
	}

	return GetDrvdScalarProps(child_index)->GetFunctionProperties();
}

BOOL
CExpressionHandle::DeriveHasNonScalarFunction(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveHasNonScalarFunction();
	}

	return GetDrvdScalarProps(child_index)->HasNonScalarFunction();
}

ULONG
CExpressionHandle::DeriveTotalDistinctAggs(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveTotalDistinctAggs();
	}

	return GetDrvdScalarProps(child_index)->GetTotalDistinctAggs();
}

BOOL
CExpressionHandle::DeriveHasMultipleDistinctAggs(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveHasMultipleDistinctAggs();
	}

	return GetDrvdScalarProps(child_index)->HasMultipleDistinctAggs();
}

BOOL
CExpressionHandle::DeriveHasScalarArrayCmp(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveHasScalarArrayCmp();
	}

	return GetDrvdScalarProps(child_index)->HasScalarArrayCmp();
}

BOOL
CExpressionHandle::DeriveHasScalarFuncProject(ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]->DeriveHasScalarFuncProject();
	}

	return GetDrvdScalarProps(child_index)->HasScalarFuncProject();
}

BOOL
CExpressionHandle::DeriveContainsOnlyReplicationSafeAggFuncs(
	ULONG child_index) const
{
	if (nullptr != Pexpr())
	{
		return (*Pexpr())[child_index]
			->DeriveContainsOnlyReplicationSafeAggFuncs();
	}

	return GetDrvdScalarProps(child_index)
		->ContainsOnlyReplicationSafeAggFuncs();
}
// EOF
