greenplumn CGroupExpression 源码

  • 2022-08-18
  • 浏览 (382)

greenplumn CGroupExpression 代码

文件路径:/src/backend/gporca/libgpopt/src/search/CGroupExpression.cpp

//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2009 Greenplum, Inc.
//
//	@filename:
//		CGroupExpression.cpp
//
//	@doc:
//		Implementation of group expressions
//---------------------------------------------------------------------------

#include "gpopt/search/CGroupExpression.h"

#include "gpos/base.h"
#include "gpos/error/CAutoTrace.h"
#include "gpos/io/COstreamString.h"
#include "gpos/string/CWStringDynamic.h"
#include "gpos/task/CAutoSuspendAbort.h"
#include "gpos/task/CWorker.h"

#include "gpopt/base/COptCtxt.h"
#include "gpopt/base/COptimizationContext.h"
#include "gpopt/base/CUtils.h"
#include "gpopt/operators/CPhysicalAgg.h"
#include "gpopt/optimizer/COptimizerConfig.h"
#include "gpopt/search/CBinding.h"
#include "gpopt/search/CGroupProxy.h"
#include "gpopt/xforms/CXformFactory.h"
#include "gpopt/xforms/CXformUtils.h"
#include "naucrates/traceflags/traceflags.h"

using namespace gpopt;

FORCE_GENERATE_DBGSTR(CGroupExpression);

#define GPOPT_COSTCTXT_HT_BUCKETS 100

// invalid group expression
const CGroupExpression CGroupExpression::m_gexprInvalid{};


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::CGroupExpression
//
//	@doc:
//		ctor
//
//---------------------------------------------------------------------------
CGroupExpression::CGroupExpression(CMemoryPool *mp, COperator *pop,
								   CGroupArray *pdrgpgroup,
								   CXform::EXformId exfid,
								   CGroupExpression *pgexprOrigin,
								   BOOL fIntermediate)
	: m_pgexprDuplicate(nullptr),
	  m_pop(pop),
	  m_pdrgpgroup(pdrgpgroup),

	  m_exfidOrigin(exfid),
	  m_pgexprOrigin(pgexprOrigin),
	  m_fIntermediate(fIntermediate),

	  m_ecirculardependency(ecdDefault)
{
	GPOS_ASSERT(nullptr != pop);
	GPOS_ASSERT(nullptr != pdrgpgroup);
	GPOS_ASSERT_IMP(exfid != CXform::ExfInvalid, nullptr != pgexprOrigin);

	// store sorted array of children for faster comparison
	if (1 < pdrgpgroup->Size() && !pop->FInputOrderSensitive())
	{
		m_pdrgpgroupSorted = GPOS_NEW(mp) CGroupArray(mp, pdrgpgroup->Size());
		m_pdrgpgroupSorted->AppendArray(pdrgpgroup);
		m_pdrgpgroupSorted->Sort();

		GPOS_ASSERT(m_pdrgpgroupSorted->IsSorted());
	}

	m_ppartialplancostmap = GPOS_NEW(mp) PartialPlanToCostMap(mp);

	// initialize cost contexts hash table
	m_sht.Init(mp, GPOPT_COSTCTXT_HT_BUCKETS, GPOS_OFFSET(CCostContext, m_link),
			   GPOS_OFFSET(CCostContext, m_poc),
			   &(COptimizationContext::m_pocInvalid),
			   COptimizationContext::HashValue, COptimizationContext::Equals);
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::~CGroupExpression
//
//	@doc:
//		Dtor
//
//---------------------------------------------------------------------------
CGroupExpression::~CGroupExpression()
{
	if (this != &(CGroupExpression::m_gexprInvalid))
	{
		CleanupContexts();

		m_pop->Release();
		m_pdrgpgroup->Release();

		CRefCount::SafeRelease(m_pdrgpgroupSorted);
		m_ppartialplancostmap->Release();
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::CleanupContexts
//
//	@doc:
//		 Destroy stored cost contexts in hash table
//
//---------------------------------------------------------------------------
void
CGroupExpression::CleanupContexts()
{
	// need to suspend cancellation while cleaning up
	{
		CAutoSuspendAbort asa;

		ShtIter shtit(m_sht);
		CCostContext *pcc = nullptr;
		while (nullptr != pcc || shtit.Advance())
		{
			if (nullptr != pcc)
			{
				pcc->Release();
			}

			// iter's accessor scope
			{
				ShtAccIter shtitacc(shtit);
				if (nullptr != (pcc = shtitacc.Value()))
				{
					shtitacc.Remove(pcc);
				}
			}
		}
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::Init
//
//	@doc:
//		Init group expression
//
//
//---------------------------------------------------------------------------
void
CGroupExpression::Init(CGroup *pgroup, ULONG id)
{
	SetGroup(pgroup);
	SetId(id);
	SetOptimizationLevel();
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::SetOptimizationLevel
//
//	@doc:
//		Set optimization level of group expression
//
//
//---------------------------------------------------------------------------
void
CGroupExpression::SetOptimizationLevel()
{
	// a sequence expression with a first child group that contains a CTE
	// producer gets a higher optimization level. This is to be sure that the
	// producer gets optimized before its consumers
	if (COperator::EopPhysicalSequence == m_pop->Eopid())
	{
		CGroup *pgroupFirst = (*this)[0];
		if (pgroupFirst->FHasCTEProducer())
		{
			m_eol = EolHigh;
		}
	}
	else if (CUtils::FHashJoin(m_pop))
	{
		// optimize hash join first to minimize plan cost quickly
		m_eol = EolHigh;
	}
	else if (CUtils::FPhysicalAgg(m_pop))
	{
		BOOL fPreferMultiStageAgg = GPOS_FTRACE(EopttraceForceMultiStageAgg);
		if (!fPreferMultiStageAgg &&
			COperator::EopPhysicalHashAgg == m_pop->Eopid())
		{
			// if we choose agg plans based on cost only (no preference for multi-stage agg),
			// we optimize hash agg first to to minimize plan cost quickly
			m_eol = EolHigh;
			return;
		}

		// if we only want plans with multi-stage agg, we generate multi-stage agg
		// first to avoid later optimization of one stage agg if possible
		BOOL fMultiStage = CPhysicalAgg::PopConvert(m_pop)->FMultiStage();
		if (fPreferMultiStageAgg && fMultiStage)
		{
			// optimize multi-stage agg first to allow avoiding one-stage agg if possible
			m_eol = EolHigh;
		}
	}
}

//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::FValidContext
//
//	@doc:
//		Check if group expression is valid with respect to given child contexts
//
//		This is called during cost computation phase in group expression
//		optimization after enforcement is complete. Since it is called bottom-up,
//		for the given physical group expression, all the derived properties are
//		already computed.
//
//		Since property enforcement in CEngine::FCheckEnfdProps() only determines
//		whether or not an enforcer is added to the group, it is possible for the
//		enforcer group expression to select a child group expression that did not
//		create the enforcer. This could lead to invalid plans that could not have
//		been prevented earlier because derived physical properties weren't
//		available. For example, a Motion group expression may select as a child a
//		DynamicTableScan that has unresolved part propagators, instead of picking
//		the PartitionSelector enforcer which would resolve it.
//
//		This method can be used to reject such plans.
//
//---------------------------------------------------------------------------
BOOL
CGroupExpression::FValidContext(CMemoryPool *mp, COptimizationContext *poc,
								COptimizationContextArray *pdrgpocChild)
{
	GPOS_ASSERT(m_pop->FPhysical());

	return CPhysical::PopConvert(m_pop)->FValidContext(mp, poc, pdrgpocChild);
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::SetId
//
//	@doc:
//		Set id of expression
//
//---------------------------------------------------------------------------
void
CGroupExpression::SetId(ULONG id)
{
	GPOS_ASSERT(GPOPT_INVALID_GEXPR_ID == m_id);

	m_id = id;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::SetGroup
//
//	@doc:
//		Set group pointer of expression
//
//---------------------------------------------------------------------------
void
CGroupExpression::SetGroup(CGroup *pgroup)
{
	GPOS_ASSERT(nullptr == m_pgroup);
	GPOS_ASSERT(nullptr != pgroup);

	m_pgroup = pgroup;
}

//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::FCostContextExists
//
//	@doc:
//		Check if cost context already exists in group expression hash table
//
//---------------------------------------------------------------------------
BOOL
CGroupExpression::FCostContextExists(COptimizationContext *poc,
									 COptimizationContextArray *pdrgpoc)
{
	GPOS_ASSERT(nullptr != poc);

	// lookup context based on required properties
	CCostContext *pccFound = nullptr;
	{
		ShtAcc shta(Sht(), poc);
		pccFound = shta.Find();
	}

	while (nullptr != pccFound)
	{
		if (COptimizationContext::FEqualContextIds(pdrgpoc,
												   pccFound->Pdrgpoc()))
		{
			// a cost context, matching required properties and child contexts, was already created
			return true;
		}

		{
			ShtAcc shta(Sht(), poc);
			pccFound = shta.Next(pccFound);
		}
	}

	return false;
}


//---------------------------------------------------------------------------
//     @function:
//			CGroupExpression::PccRemove
//
//     @doc:
//			Remove cost context in hash table;
//
//---------------------------------------------------------------------------
CCostContext *
CGroupExpression::PccRemove(COptimizationContext *poc, ULONG ulOptReq)
{
	GPOS_ASSERT(nullptr != poc);
	ShtAcc shta(Sht(), poc);
	CCostContext *pccFound = shta.Find();
	while (nullptr != pccFound)
	{
		if (ulOptReq == pccFound->UlOptReq())
		{
			shta.Remove(pccFound);
			return pccFound;
		}

		pccFound = shta.Next(pccFound);
	}

	return nullptr;
}


//---------------------------------------------------------------------------
//     @function:
//			CGroupExpression::PccInsertBest
//
//     @doc:
//			Insert given context in hash table only if a better context
//			does not already exist,
//			return the context that is kept in hash table
//
//---------------------------------------------------------------------------
CCostContext *
CGroupExpression::PccInsertBest(CCostContext *pcc)
{
	GPOS_ASSERT(nullptr != pcc);

	COptimizationContext *poc = pcc->Poc();
	const ULONG ulOptReq = pcc->UlOptReq();

	// remove existing cost context, if any
	CCostContext *pccExisting = PccRemove(poc, ulOptReq);
	CCostContext *pccKept = nullptr;

	// compare existing context with given context
	if (nullptr == pccExisting || pcc->FBetterThan(pccExisting))
	{
		// insert new context
		pccKept = PccInsert(pcc);
		GPOS_ASSERT(pccKept == pcc);

		if (nullptr != pccExisting)
		{
			if (pccExisting == poc->PccBest())
			{
				// change best cost context of the corresponding optimization context
				poc->SetBest(pcc);
			}
			pccExisting->Release();
		}
	}
	else
	{
		// re-insert existing context
		pcc->Release();
		pccKept = PccInsert(pccExisting);
		GPOS_ASSERT(pccKept == pccExisting);
	}

	return pccKept;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PccComputeCost
//
//	@doc:
//		Compute and store expression's cost under a given context;
//		the function returns the cost context containing the computed cost
//
//---------------------------------------------------------------------------
CCostContext *
CGroupExpression::PccComputeCost(
	CMemoryPool *mp, COptimizationContext *poc, ULONG ulOptReq,
	COptimizationContextArray *pdrgpoc,	 // array of child contexts
	BOOL fPruned,  // is created cost context pruned based on cost bound
	CCost
		costLowerBound	// lower bound on the cost of plan carried by cost context
)
{
	GPOS_ASSERT(nullptr != poc);
	GPOS_ASSERT_IMP(!fPruned, nullptr != pdrgpoc);

	if (!fPruned && !FValidContext(mp, poc, pdrgpoc))
	{
		return nullptr;
	}

	// check if the same cost context is already created for current group expression
	if (FCostContextExists(poc, pdrgpoc))
	{
		return nullptr;
	}

	poc->AddRef();
	this->AddRef();
	CCostContext *pcc = GPOS_NEW(mp) CCostContext(mp, poc, ulOptReq, this);
	BOOL fValid = true;

	// computing cost
	pcc->SetState(CCostContext::estCosting);

	if (!fPruned)
	{
		if (nullptr != pdrgpoc)
		{
			pdrgpoc->AddRef();
		}
		pcc->SetChildContexts(pdrgpoc);

		fValid = pcc->IsValid(mp);
		if (fValid)
		{
			CCost cost = CostCompute(mp, pcc);
			pcc->SetCost(cost);
		}
		GPOS_ASSERT_IMP(COptCtxt::FAllEnforcersEnabled(),
						fValid && "Cost context carries an invalid plan");
	}
	else
	{
		pcc->SetPruned();
		pcc->SetCost(costLowerBound);
	}

	pcc->SetState(CCostContext::estCosted);
	if (fValid)
	{
		return PccInsertBest(pcc);
	}

	pcc->Release();

	// invalid cost context
	return nullptr;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::CostLowerBound
//
//	@doc:
//		Compute a lower bound on plans rooted by current group expression for
//		the given required properties
//
//---------------------------------------------------------------------------
CCost
CGroupExpression::CostLowerBound(CMemoryPool *mp, CReqdPropPlan *prppInput,
								 CCostContext *pccChild, ULONG child_index)
{
	GPOS_ASSERT(nullptr != prppInput);
	GPOS_ASSERT(Pop()->FPhysical());

	prppInput->AddRef();
	if (nullptr != pccChild)
	{
		pccChild->AddRef();
	}
	CPartialPlan *ppp =
		GPOS_NEW(mp) CPartialPlan(this, prppInput, pccChild, child_index);
	CCost *pcostLowerBound = m_ppartialplancostmap->Find(ppp);
	if (nullptr != pcostLowerBound)
	{
		ppp->Release();
		return *pcostLowerBound;
	}

	// compute partial plan cost
	CCost cost = ppp->CostCompute(mp);

	BOOL fSuccess GPOS_ASSERTS_ONLY =
		m_ppartialplancostmap->Insert(ppp, GPOS_NEW(mp) CCost(cost.Get()));
	GPOS_ASSERT(fSuccess);

	return cost;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::SetState
//
//	@doc:
//		Set group expression state;
//
//---------------------------------------------------------------------------
void
CGroupExpression::SetState(EState estNewState)
{
	GPOS_ASSERT(estNewState == (EState)(m_estate + 1));

	m_estate = estNewState;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::ResetState
//
//	@doc:
//		Reset group expression state;
//
//---------------------------------------------------------------------------
void
CGroupExpression::ResetState()
{
	m_estate = estUnexplored;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::CostCompute
//
//	@doc:
//		Costing scheme.
//
//---------------------------------------------------------------------------
CCost
CGroupExpression::CostCompute(CMemoryPool *mp, CCostContext *pcc)
{
	GPOS_ASSERT(nullptr != pcc);

	// prepare cost array
	COptimizationContextArray *pdrgpoc = pcc->Pdrgpoc();
	CCostArray *pdrgpcostChildren = GPOS_NEW(mp) CCostArray(mp);
	const ULONG length = pdrgpoc->Size();
	for (ULONG ul = 0; ul < length; ul++)
	{
		COptimizationContext *pocChild = (*pdrgpoc)[ul];
		pdrgpcostChildren->Append(GPOS_NEW(mp)
									  CCost(pocChild->PccBest()->Cost()));
	}

	CCost cost = pcc->CostCompute(mp, pdrgpcostChildren);
	pdrgpcostChildren->Release();

	return cost;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::FTransitioned
//
//	@doc:
//		Check if transition to the given state is completed;
//
//---------------------------------------------------------------------------
BOOL
CGroupExpression::FTransitioned(EState estate) const
{
	GPOS_ASSERT(estate == estExplored || estate == estImplemented);

	return !Pop()->FLogical() || (estate == estExplored && FExplored()) ||
		   (estate == estImplemented && FImplemented());
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PccLookup
//
//	@doc:
//		Lookup cost context in hash table;
//
//---------------------------------------------------------------------------
CCostContext *
CGroupExpression::PccLookup(COptimizationContext *poc, ULONG ulOptReq)
{
	GPOS_ASSERT(nullptr != poc);

	ShtAcc shta(Sht(), poc);
	CCostContext *pccFound = shta.Find();
	while (nullptr != pccFound)
	{
		if (ulOptReq == pccFound->UlOptReq())
		{
			return pccFound;
		}

		pccFound = shta.Next(pccFound);
	}

	return nullptr;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PccLookupAll
//
//	@doc:
//		Lookup all valid cost contexts matching given optimization context
//
//---------------------------------------------------------------------------
CCostContextArray *
CGroupExpression::PdrgpccLookupAll(CMemoryPool *mp, COptimizationContext *poc)
{
	GPOS_ASSERT(nullptr != poc);
	CCostContextArray *pdrgpcc = GPOS_NEW(mp) CCostContextArray(mp);

	CCostContext *pccFound = nullptr;
	BOOL fValid = false;
	{
		ShtAcc shta(Sht(), poc);
		pccFound = shta.Find();
		fValid =
			(nullptr != pccFound && pccFound->Cost() != GPOPT_INVALID_COST &&
			 !pccFound->FPruned());
	}

	while (nullptr != pccFound)
	{
		if (fValid)
		{
			pccFound->AddRef();
			pdrgpcc->Append(pccFound);
		}

		{
			ShtAcc shta(Sht(), poc);
			pccFound = shta.Next(pccFound);
			fValid = (nullptr != pccFound &&
					  pccFound->Cost() != GPOPT_INVALID_COST &&
					  !pccFound->FPruned());
		}
	}

	return pdrgpcc;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PccInsert
//
//	@doc:
//		Insert a cost context in hash table;
//
//---------------------------------------------------------------------------
CCostContext *
CGroupExpression::PccInsert(CCostContext *pcc)
{
	// HERE BE DRAGONS
	// See comment in CCache::InsertEntry
	COptimizationContext *const poc = pcc->Poc();
	ShtAcc shta(Sht(), poc);

	CCostContext *pccFound = shta.Find();
	while (nullptr != pccFound)
	{
		if (CCostContext::Equals(*pcc, *pccFound))
		{
			return pccFound;
		}
		pccFound = shta.Next(pccFound);
	}
	GPOS_ASSERT(nullptr == pccFound);

	shta.Insert(pcc);
	return pcc;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PreprocessTransform
//
//	@doc:
//		Pre-processing before applying transformation
//
//---------------------------------------------------------------------------
void
CGroupExpression::PreprocessTransform(CMemoryPool *pmpLocal,
									  CMemoryPool *pmpGlobal, CXform *pxform)
{
	if (CXformUtils::FDeriveStatsBeforeXform(pxform) &&
		nullptr == Pgroup()->Pstats())
	{
		GPOS_ASSERT(Pgroup()->FStatsDerivable(pmpGlobal));

		// derive stats on container group before applying xform
		CExpressionHandle exprhdl(pmpGlobal);
		exprhdl.Attach(this);
		exprhdl.DeriveStats(pmpLocal, pmpGlobal, nullptr /*prprel*/,
							nullptr /*stats_ctxt*/);
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PostprocessTransform
//
//	@doc:
//		Post-processing after applying transformation
//
//---------------------------------------------------------------------------
void
CGroupExpression::PostprocessTransform(CMemoryPool *,  // pmpLocal
									   CMemoryPool *,  // pmpGlobal
									   CXform *pxform) const
{
	if (CXformUtils::FDeriveStatsBeforeXform(pxform))
	{
		(void) Pgroup()->FResetStats();
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::Transform
//
//	@doc:
//		Transform group expression using the given xform
//
//---------------------------------------------------------------------------
void
CGroupExpression::Transform(
	CMemoryPool *mp, CMemoryPool *pmpLocal, CXform *pxform,
	CXformResult *pxfres,
	ULONG *pulElapsedTime,	// output: elapsed time in millisecond
	ULONG *pulNumberOfBindings)
{
	GPOS_ASSERT(nullptr != pulElapsedTime);
	GPOS_CHECK_ABORT;

	BOOL fPrintOptStats = GPOS_FTRACE(EopttracePrintOptimizationStatistics);
	CTimerUser timer;
	if (fPrintOptStats)
	{
		timer.Restart();
	}

	*pulElapsedTime = 0;
	// check traceflag and compatibility with origin xform
	if (GPOPT_FDISABLED_XFORM(pxform->Exfid()) ||
		!pxform->FCompatible(m_exfidOrigin))
	{
		if (fPrintOptStats)
		{
			*pulElapsedTime = timer.ElapsedMS();
		}
		return;
	}

	// check xform promise
	CExpressionHandle exprhdl(mp);
	exprhdl.Attach(this);
	exprhdl.DeriveProps(nullptr /*pdpctxt*/);
	if (CXform::ExfpNone == pxform->Exfp(exprhdl))
	{
		if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
		{
			*pulElapsedTime = timer.ElapsedMS();
		}
		return;
	}

	// pre-processing before applying xform to group expression
	PreprocessTransform(pmpLocal, mp, pxform);

	// extract memo bindings to apply xform
	CBinding binding;
	CXformContext *pxfctxt = GPOS_NEW(mp) CXformContext(mp);

	COptimizerConfig *optconfig =
		COptCtxt::PoctxtFromTLS()->GetOptimizerConfig();
	ULONG bindThreshold = optconfig->GetHint()->UlXformBindThreshold();
	CExpression *pexprPattern = pxform->PexprPattern();
	CExpression *pexpr = binding.PexprExtract(mp, this, pexprPattern, nullptr);
	while (nullptr != pexpr)
	{
		++(*pulNumberOfBindings);
		ULONG ulNumResults = pxfres->Pdrgpexpr()->Size();
		pxform->Transform(pxfctxt, pxfres, pexpr);
		ulNumResults = pxfres->Pdrgpexpr()->Size() - ulNumResults;
		PrintXform(mp, pxform, pexpr, pxfres, ulNumResults);

		if ((bindThreshold != 0 && (*pulNumberOfBindings) > bindThreshold) ||
			pxform->IsApplyOnce() ||
			(0 < pxfres->Pdrgpexpr()->Size() &&
			 !CXformUtils::FApplyToNextBinding(pxform, pexpr)))
		{
			// do not apply xform to other possible patterns
			pexpr->Release();
			break;
		}

		CExpression *pexprLast = pexpr;
		pexpr = binding.PexprExtract(mp, this, pexprPattern, pexprLast);

		// release last extracted expression
		pexprLast->Release();

		GPOS_CHECK_ABORT;
	}
	pxfctxt->Release();

	// post-prcoessing before applying xform to group expression
	PostprocessTransform(pmpLocal, mp, pxform);

	if (fPrintOptStats)
	{
		*pulElapsedTime = timer.ElapsedMS();
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::FMatchNonScalarChildren
//
//	@doc:
//		Match children of group expression against given children of
//		passed expression
//
//---------------------------------------------------------------------------
BOOL
CGroupExpression::FMatchNonScalarChildren(const CGroupExpression *pgexpr) const
{
	GPOS_ASSERT(nullptr != pgexpr);

	if (0 == Arity())
	{
		return (pgexpr->Arity() == 0);
	}

	return CGroup::FMatchNonScalarGroups(m_pdrgpgroup, pgexpr->m_pdrgpgroup);
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::Matches
//
//	@doc:
//		Match group expression against given operator and its children
//
//---------------------------------------------------------------------------
BOOL
CGroupExpression::Matches(const CGroupExpression *pgexpr) const
{
	GPOS_ASSERT(nullptr != pgexpr);

	// make sure we are not comparing to invalid group expression
	if (nullptr == this->Pop() || nullptr == pgexpr->Pop())
	{
		return nullptr == this->Pop() && nullptr == pgexpr->Pop();
	}

	// have same arity
	if (Arity() != pgexpr->Arity())
	{
		return false;
	}

	// match operators
	if (!m_pop->Matches(pgexpr->m_pop))
	{
		return false;
	}

	// compare inputs
	if (0 == Arity())
	{
		return true;
	}
	else
	{
		if (1 == Arity() || m_pop->FInputOrderSensitive())
		{
			return CGroup::FMatchGroups(m_pdrgpgroup, pgexpr->m_pdrgpgroup);
		}
		else
		{
			GPOS_ASSERT(nullptr != m_pdrgpgroupSorted &&
						nullptr != pgexpr->m_pdrgpgroupSorted);

			return CGroup::FMatchGroups(m_pdrgpgroupSorted,
										pgexpr->m_pdrgpgroupSorted);
		}
	}

	GPOS_ASSERT(!"Unexpected exit from function");
	return false;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::HashValue
//
//	@doc:
//		static hash function for operator and group references
//
//---------------------------------------------------------------------------
ULONG
CGroupExpression::HashValue(COperator *pop, CGroupArray *pdrgpgroup)
{
	GPOS_ASSERT(nullptr != pop);
	GPOS_ASSERT(nullptr != pdrgpgroup);

	ULONG ulHash = pop->HashValue();

	ULONG arity = pdrgpgroup->Size();
	for (ULONG i = 0; i < arity; i++)
	{
		ulHash = CombineHashes(ulHash, (*pdrgpgroup)[i]->HashValue());
	}

	return ulHash;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::HashValue
//
//	@doc:
//		static hash function for group expressions
//
//---------------------------------------------------------------------------
ULONG
CGroupExpression::HashValue(const CGroupExpression &gexpr)
{
	return gexpr.HashValue();
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PstatsRecursiveDerive
//
//	@doc:
//		Derive stats recursively on group expression
//
//---------------------------------------------------------------------------
IStatistics *
CGroupExpression::PstatsRecursiveDerive(CMemoryPool *,	// pmpLocal
										CMemoryPool *pmpGlobal,
										CReqdPropRelational *prprel,
										IStatisticsArray *stats_ctxt,
										BOOL fComputeRootStats)
{
	GPOS_ASSERT(!Pgroup()->FScalar());
	GPOS_ASSERT(!Pgroup()->FImplemented());
	GPOS_ASSERT(nullptr != stats_ctxt);
	GPOS_CHECK_ABORT;

	// trigger recursive property derivation
	CExpressionHandle exprhdl(pmpGlobal);
	exprhdl.Attach(this);
	exprhdl.DeriveProps(nullptr /*pdpctxt*/);

	// compute required relational properties on child groups
	exprhdl.ComputeReqdProps(prprel, 0 /*ulOptReq*/);

	// trigger recursive stat derivation
	exprhdl.DeriveStats(stats_ctxt, fComputeRootStats);
	IStatistics *stats = exprhdl.Pstats();
	if (nullptr != stats)
	{
		stats->AddRef();
	}

	return stats;
}


//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::PrintXform
//
//	@doc:
//		Print transformation
//
//---------------------------------------------------------------------------
void
CGroupExpression::PrintXform(CMemoryPool *mp, CXform *pxform,
							 CExpression *pexpr, CXformResult *pxfres,
							 ULONG ulNumResults)
{
	if (nullptr != pexpr && GPOS_FTRACE(EopttracePrintXform) &&
		GPOS_FTRACE(EopttracePrintXformResults))
	{
		CAutoTrace at(mp);
		IOstream &os(at.Os());

		os << *pxform << std::endl
		   << "Input:" << std::endl
		   << *pexpr << "Output:" << std::endl
		   << "Alternatives:" << std::endl;
		CExpressionArray *pdrgpexpr = pxfres->Pdrgpexpr();
		ULONG ulStart = pdrgpexpr->Size() - ulNumResults;
		ULONG end = pdrgpexpr->Size();

		for (ULONG i = ulStart; i < end; i++)
		{
			os << i - ulStart << ": " << std::endl;
			(*pdrgpexpr)[i]->OsPrint(os);
		}
	}
}

//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::OsPrintCostContexts
//
//	@doc:
//		Print group expression cost contexts
//
//---------------------------------------------------------------------------
IOstream &
CGroupExpression::OsPrintCostContexts(IOstream &os, const CHAR *szPrefix) const
{
	if (Pop()->FPhysical() && GPOS_FTRACE(EopttracePrintOptimizationContext))
	{
		// print cost contexts
		os << szPrefix << szPrefix << "Cost Ctxts:" << std::endl;
		CCostContext *pcc = nullptr;
		ShtIter shtit(const_cast<CGroupExpression *>(this)->Sht());
		while (shtit.Advance())
		{
			{
				ShtAccIter shtitacc(shtit);
				pcc = shtitacc.Value();
			}

			if (nullptr != pcc)
			{
				os << szPrefix << szPrefix << szPrefix;
				(void) pcc->OsPrint(os);
			}
		}
	}

	return os;
}


// Consider the group expression CLogicalSelect [ 0 3 ]
// it has the 0th child coming from Group 0, where Group 0 has Duplicate Group 4
// While deriving Stats, this will cause a circular loop as follows
// 1. CLogicalSelect child 0 -> Will ask the stats to be derived on Group 0
// 2. Group 0 will ask Group 4 to give the stats (as its duplicate),
// which will then again ask CLogicalSelect [0 4] to derive stats resulting in a loop.
// Such Group Expression can be ignored for deriving stats and implementation.
// Group 4 (#GExprs: 5):
// 0: CLogicalSelect [ 0 3 ]
// 1: CLogicalNAryJoin [ 6 7 8 ] Origin: (xform: CXformInlineCTEConsumerUnderSelect, Grp: 4, GrpExpr: 0)
// 2: CLogicalCTEConsumer (0), Columns: ["a" (18), "b" (19), "a" (20), "b" (21)] [ ]
// 3: CLogicalNAryJoin [ 6 7 3 ] Origin: (xform: CXformInlineCTEConsumer, Grp: 4, GrpExpr: 2)
// 4: CLogicalInnerJoin [ 6 7 3 ] Origin: (xform: CXformExpandNAryJoinGreedy, Grp: 4, GrpExpr: 3)
//
// Group 0 (#GExprs: 0, Duplicate Group: 4):
BOOL
CGroupExpression::ContainsCircularDependencies()
{
	// if it's already marked to contain circular dependency, return early
	if (m_ecirculardependency == CGroupExpression::ecdCircularDependency)
	{
		return true;
	}

	GPOS_ASSERT(m_ecirculardependency == CGroupExpression::ecdDefault);

	// check if there are any circular dependencies
	CGroupArray *child_groups = Pdrgpgroup();
	for (ULONG ul = 0; ul < child_groups->Size(); ul++)
	{
		CGroup *child_group = (*child_groups)[ul];
		if (child_group->FScalar())
		{
			continue;
		}
		CGroup *child_duplicate_group = child_group->PgroupDuplicate();
		if (child_duplicate_group != nullptr)
		{
			ULONG child_duplicate_group_id = child_duplicate_group->Id();
			ULONG current_group_id = Pgroup()->Id();
			if (child_duplicate_group_id == current_group_id)
			{
				m_ecirculardependency = CGroupExpression::ecdCircularDependency;
				GPOS_ASSERT(Pgroup()->UlGExprs() > 1);
				break;
			}
		}
	}

	return m_ecirculardependency == CGroupExpression::ecdCircularDependency;
}

//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::OsPrint
//
//	@doc:
//		Print function
//
//---------------------------------------------------------------------------
IOstream &
CGroupExpression::OsPrint(IOstream &os) const
{
	return OsPrintWithPrefix(os, "");
}

IOstream &
CGroupExpression::OsPrintWithPrefix(IOstream &os, const CHAR *szPrefix) const
{
	os << szPrefix << m_id << ": ";
	(void) m_pop->OsPrint(os);

	if (EolHigh == m_eol)
	{
		os << " (High)";
	}
	os << " [ ";

	ULONG arity = Arity();
	for (ULONG i = 0; i < arity; i++)
	{
		os << (*m_pdrgpgroup)[i]->Id() << " ";
	}
	os << "]";

	if (nullptr != m_pgexprDuplicate)
	{
		os << " Dup. of GrpExpr " << m_pgexprDuplicate->Id() << " in Grp "
		   << m_pgexprDuplicate->Pgroup()->Id();
	}

	if (GPOS_FTRACE(EopttracePrintXform) && ExfidOrigin() != CXform::ExfInvalid)
	{
		os << " Origin: ";
		if (m_fIntermediate)
		{
			os << "intermediate result of ";
		}
		os << "(xform: " << CXformFactory::Pxff()->Pxf(ExfidOrigin())->SzId();
		os << ", Grp: " << m_pgexprOrigin->Pgroup()->Id()
		   << ", GrpExpr: " << m_pgexprOrigin->Id() << ")";
	}
	os << std::endl;

	(void) OsPrintCostContexts(os, szPrefix);

	return os;
}

#ifdef GPOS_DEBUG
//---------------------------------------------------------------------------
//	@function:
//		CGroupExpression::DbgPrint
//
//	@doc:
//		Print driving function for use in interactive debugging;
//		always prints to stderr;
//
//---------------------------------------------------------------------------
void
CGroupExpression::DbgPrintWithProperties() const
{
	CAutoTraceFlag atf(EopttracePrintGroupProperties, true);
	CAutoTrace at(CTask::Self()->Pmp());
	(void) this->OsPrint(at.Os());
}
#endif	// GPOS_DEBUG

// EOF

相关信息

greenplumn 源码目录

相关文章

greenplumn CBinding 源码

greenplumn CGroup 源码

greenplumn CGroupProxy 源码

greenplumn CJob 源码

greenplumn CJobFactory 源码

greenplumn CJobGroup 源码

greenplumn CJobGroupExploration 源码

greenplumn CJobGroupExpression 源码

greenplumn CJobGroupExpressionExploration 源码

greenplumn CJobGroupExpressionImplementation 源码

0  赞