greenplumn CCostContext 源码

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

greenplumn CCostContext 代码

文件路径:/src/backend/gporca/libgpopt/src/base/CCostContext.cpp

//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2011 EMC Corp.
//
//	@filename:
//		CCostContext.cpp
//
//	@doc:
//		Implementation of cost context
//---------------------------------------------------------------------------

#include "gpopt/base/CCostContext.h"

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

#include "gpopt/base/CDistributionSpecHashed.h"
#include "gpopt/base/CDrvdPropCtxtPlan.h"
#include "gpopt/base/CDrvdPropCtxtRelational.h"
#include "gpopt/base/CDrvdPropPlan.h"
#include "gpopt/base/COptCtxt.h"
#include "gpopt/cost/ICostModel.h"
#include "gpopt/exception.h"
#include "gpopt/operators/CExpressionHandle.h"
#include "gpopt/operators/CPhysicalAgg.h"
#include "gpopt/operators/CPhysicalDynamicIndexScan.h"
#include "gpopt/operators/CPhysicalDynamicTableScan.h"
#include "gpopt/operators/CPhysicalSpool.h"
#include "gpopt/optimizer/COptimizerConfig.h"
#include "gpopt/search/CGroupExpression.h"
#include "naucrates/statistics/CStatisticsUtils.h"

using namespace gpopt;
using namespace gpnaucrates;

FORCE_GENERATE_DBGSTR(CCostContext);

//---------------------------------------------------------------------------
//	@function:
//		CCostContext::CCostContext
//
//	@doc:
//		Ctor
//
//---------------------------------------------------------------------------
CCostContext::CCostContext(CMemoryPool *mp, COptimizationContext *poc,
						   ULONG ulOptReq, CGroupExpression *pgexpr)
	: m_mp(mp),
	  m_cost(GPOPT_INVALID_COST),
	  m_estate(estUncosted),
	  m_pgexpr(pgexpr),
	  m_pgexprForStats(nullptr),
	  m_pdrgpoc(nullptr),
	  m_pdpplan(nullptr),
	  m_ulOptReq(ulOptReq),
	  m_fPruned(false),
	  m_pstats(nullptr),
	  m_poc(poc)
{
	GPOS_ASSERT(nullptr != poc);
	GPOS_ASSERT(nullptr != pgexpr);
	GPOS_ASSERT_IMP(
		pgexpr->Pop()->FPhysical(),
		ulOptReq < CPhysical::PopConvert(pgexpr->Pop())->UlOptRequests());

	if (!m_pgexpr->Pop()->FScalar() &&
		!CPhysical::PopConvert(m_pgexpr->Pop())->FPassThruStats())
	{
		CGroupExpression *pgexprForStats =
			m_pgexpr->Pgroup()->PgexprBestPromise(m_mp, m_pgexpr);
		if (nullptr != pgexprForStats)
		{
			pgexprForStats->AddRef();
			m_pgexprForStats = pgexprForStats;
		}
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::~CCostContext
//
//	@doc:
//		Dtor
//
//---------------------------------------------------------------------------
CCostContext::~CCostContext()
{
	CRefCount::SafeRelease(m_pgexpr);
	CRefCount::SafeRelease(m_pgexprForStats);
	CRefCount::SafeRelease(m_poc);
	CRefCount::SafeRelease(m_pdrgpoc);
	CRefCount::SafeRelease(m_pdpplan);
	CRefCount::SafeRelease(m_pstats);
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::FOwnsStats
//
//	@doc:
//		Check if new stats are owned by this context
//
//---------------------------------------------------------------------------
BOOL
CCostContext::FOwnsStats() const
{
	GPOS_ASSERT(nullptr != m_pstats);

	// new stats are owned if context holds stats different from group stats
	return (m_pstats != m_pgexpr->Pgroup()->Pstats());
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::FNeedsNewStats
//
//	@doc:
//		Check if we need to derive new stats for this context,
//		by default a cost context inherits stats from the owner group,
//		the only current exception is when part of the plan below cost
//		context is affected by partition elimination done by partition
//		selection in some other part of the plan
//
//---------------------------------------------------------------------------
BOOL
CCostContext::FNeedsNewStats() const
{
	COperator *pop = m_pgexpr->Pop();
	if (pop->FScalar())
	{
		// return false if scalar operator
		return false;
	}

	if (!m_poc->Prpp()->Pepp()->PppsRequired()->ContainsAnyConsumers())
	{
		// All partition selectors have been resolved at this level.
		// No need to use DPE stats for the common ancestor join and
		// nodes above it, that aren't affected by the partition selector.
		return false;
	}

	if (GPOS_FTRACE(EopttraceDeriveStatsForDPE) && CUtils::FPhysicalScan(pop) &&
		CPhysicalScan::PopConvert(pop)->FDynamicScan())
	{
		// context is attached to a dynamic scan that went through
		// partition elimination in another part of the plan
		return true;
	}

	// we need to derive stats if any child has modified stats
	BOOL fDeriveStats = false;
	const ULONG arity = Pdrgpoc()->Size();
	for (ULONG ul = 0; !fDeriveStats && ul < arity; ul++)
	{
		COptimizationContext *pocChild = (*Pdrgpoc())[ul];
		CCostContext *pccChild = pocChild->PccBest();
		GPOS_ASSERT(nullptr != pccChild);

		fDeriveStats = pccChild->FOwnsStats();
	}

	return fDeriveStats;
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::DeriveStats
//
//	@doc:
//		Derive stats of owner group expression
//
//---------------------------------------------------------------------------
void
CCostContext::DeriveStats()
{
	GPOS_ASSERT(nullptr != m_pgexpr);
	GPOS_ASSERT(nullptr != m_poc);

	if (nullptr != m_pstats)
	{
		// stats are already derived
		return;
	}

	if (m_pgexpr->Pop()->FScalar())
	{
		// exit if scalar operator
		return;
	}

	CExpressionHandle exprhdl(m_mp);
	exprhdl.Attach(this);
	exprhdl.DeriveCostContextStats();
	if (nullptr == exprhdl.Pstats())
	{
		GPOS_RAISE(gpopt::ExmaGPOPT, gpopt::ExmiNoStats,
				   GPOS_WSZ_LIT("CCostContext"));
	}

	exprhdl.Pstats()->AddRef();
	m_pstats = exprhdl.Pstats();
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::DerivePlanProps
//
//	@doc:
//		Derive properties of the plan carried by cost context
//
//---------------------------------------------------------------------------
void
CCostContext::DerivePlanProps(CMemoryPool *mp)
{
	GPOS_ASSERT(nullptr != m_pdrgpoc);

	if (nullptr == m_pdpplan)
	{
		// derive properties of the plan carried by cost context
		CExpressionHandle exprhdl(mp);
		exprhdl.Attach(this);
		exprhdl.DerivePlanPropsForCostContext();
		CDrvdPropPlan *pdpplan = CDrvdPropPlan::Pdpplan(exprhdl.Pdp());
		GPOS_ASSERT(nullptr != pdpplan);

		// set derived plan properties
		pdpplan->AddRef();
		m_pdpplan = pdpplan;
		GPOS_ASSERT(nullptr != m_pdpplan);
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::operator ==
//
//	@doc:
//		Comparison operator
//
//---------------------------------------------------------------------------
BOOL
CCostContext::operator==(const CCostContext &cc) const
{
	return Equals(cc, *this);
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::IsValid
//
//	@doc:
//		Check validity by comparing derived and required properties
//
//---------------------------------------------------------------------------
BOOL
CCostContext::IsValid(CMemoryPool *mp)
{
	GPOS_ASSERT(nullptr != m_poc);
	GPOS_ASSERT(nullptr != m_pdrgpoc);

	// obtain relational properties from group
	CDrvdPropRelational *pdprel =
		CDrvdPropRelational::GetRelationalProperties(Pgexpr()->Pgroup()->Pdp());
	GPOS_ASSERT(nullptr != pdprel);

	// derive plan properties
	DerivePlanProps(mp);

	// checking for required properties satisfaction
	BOOL fValid = Poc()->Prpp()->FSatisfied(pdprel, m_pdpplan);

#ifdef GPOS_DEBUG
	if (COptCtxt::FAllEnforcersEnabled() && !fValid)
	{
		CAutoTrace at(mp);
		IOstream &os = at.Os();

		os << std::endl << "PROPERTY MISMATCH:" << std::endl;
		os << std::endl
		   << "GROUP ID: " << Pgexpr()->Pgroup()->Id() << std::endl;
		os << std::endl << "GEXPR:" << std::endl;
		Pgexpr()->OsPrint(at.Os());
		os << std::endl
		   << "REQUIRED PROPERTIES:" << std::endl
		   << *(m_poc->Prpp());
		os << std::endl
		   << "DERIVED PROPERTIES:" << std::endl
		   << *pdprel << std::endl
		   << *m_pdpplan;
	}
#endif	//GPOS_DEBUG

	return fValid;
}



//---------------------------------------------------------------------------
//	@function:
//		CCostContext::FBreakCostTiesForJoinPlan
//
//	@doc:
//		For two cost contexts with join plans of the same cost, break the
//		tie in cost values based on join depth,
//		if tie-resolution succeeded, store a pointer to preferred cost
//		context in output argument
//
//---------------------------------------------------------------------------
void
CCostContext::BreakCostTiesForJoinPlans(
	const CCostContext *pccFst, const CCostContext *pccSnd,
	CONST_COSTCTXT_PTR *ppccPrefered,  // output: preferred cost context
	BOOL *pfTiesResolved  // output: if true, tie resolution has succeeded
)
{
	GPOS_ASSERT(nullptr != pccFst);
	GPOS_ASSERT(nullptr != pccSnd);
	GPOS_ASSERT(nullptr != ppccPrefered);
	GPOS_ASSERT(nullptr != pfTiesResolved);
	GPOS_ASSERT(*(pccFst->Poc()) == *(pccSnd->Poc()));
	GPOS_ASSERT(estCosted == pccFst->Est());
	GPOS_ASSERT(estCosted == pccSnd->Est());
	GPOS_ASSERT(pccFst->Cost() == pccSnd->Cost());
	GPOS_ASSERT(CUtils::FPhysicalJoin(pccFst->Pgexpr()->Pop()));
	GPOS_ASSERT(CUtils::FPhysicalJoin(pccSnd->Pgexpr()->Pop()));

	// for two join plans with the same estimated rows in both children,
	// prefer the plan that has smaller tree depth on the inner side,
	// this is because a smaller tree depth means that row estimation
	// errors are not grossly amplified,
	// since we build a hash table/broadcast the inner side, we need
	// to have more reliable statistics on this side

	*pfTiesResolved = false;
	*ppccPrefered = nullptr;
	CDouble dRowsOuterFst =
		(*pccFst->Pdrgpoc())[0]->PccBest()->Pstats()->Rows();
	CDouble dRowsInnerFst =
		(*pccFst->Pdrgpoc())[1]->PccBest()->Pstats()->Rows();
	if (dRowsOuterFst != dRowsInnerFst)
	{
		// two children of first plan have different row estimates
		return;
	}

	CDouble dRowsOuterSnd =
		(*pccSnd->Pdrgpoc())[0]->PccBest()->Pstats()->Rows();
	CDouble dRowsInnerSnd =
		(*pccSnd->Pdrgpoc())[1]->PccBest()->Pstats()->Rows();
	if (dRowsOuterSnd != dRowsInnerSnd)
	{
		// two children of second plan have different row estimates
		return;
	}

	if (dRowsInnerFst != dRowsInnerSnd)
	{
		// children of first plan have different row estimates compared to second plan
		return;
	}

	// both plans have equal estimated rows for both children, break tie based on join depth
	*pfTiesResolved = true;
	ULONG ulOuterJoinDepthFst = CDrvdPropRelational::GetRelationalProperties(
									(*pccFst->Pgexpr())[0]->Pdp())
									->GetJoinDepth();
	ULONG ulInnerJoinDepthFst = CDrvdPropRelational::GetRelationalProperties(
									(*pccFst->Pgexpr())[1]->Pdp())
									->GetJoinDepth();
	if (ulInnerJoinDepthFst < ulOuterJoinDepthFst)
	{
		*ppccPrefered = pccFst;
	}
	else
	{
		*ppccPrefered = pccSnd;
	}
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::FBetterThan
//
//	@doc:
//		Is current context better than the given equivalent context
//		based on cost?
//
//---------------------------------------------------------------------------
BOOL
CCostContext::FBetterThan(const CCostContext *pcc) const
{
	GPOS_ASSERT(nullptr != pcc);
	GPOS_ASSERT(*m_poc == *(pcc->Poc()));
	GPOS_ASSERT(estCosted == m_estate);
	GPOS_ASSERT(estCosted == pcc->Est());

	// if three stage scalar dqa flag is enforced we should mark plans containing
	// alternatives generated by CXformSplitDQA with three stages aggs as better
	// this should be the top most rule as we want to override cost based check between
	// 3-stage scalar aggs and 2-stage scalar aggs.
	if (GPOS_FTRACE(EopttraceForceThreeStageScalarDQA))
	{
		if (CUtils::FPhysicalAgg(Pgexpr()->Pop()) &&
			CUtils::FPhysicalAgg(pcc->Pgexpr()->Pop()))
		{
			// we are only interested in aggs generated by CXformSplitDQA. If the trace flag is turned on
			// we want to favor 3-stage agg over 2-stage scalar DQA agg. So whenever there is comparison
			// between 3-stage and 2-stage cost context, we mark 2-stage as less optimal.
			// for 2-stage vs 2-stage or 3-stage vs 3-stage, we let costing decide.
			// single stage agg do not get optimized when multi-stage aggs are present,
			// refer to COptimizationContext::FOptimizeAgg.
			if (IsTwoStageScalarDQACostCtxt(this) &&
				IsThreeStageScalarDQACostCtxt(pcc))
			{
				return false;
			}
			// if the comparison is between 3-stage agg and 2-stage scalar DQA aggs generated from CXformSplitDQA,
			// always mark 3-stage agg as having the better cost context.
			// note: CXformSplitDQA will never generate a mix of scalar and non-scalar DQAs.
			if (IsThreeStageScalarDQACostCtxt(this) &&
				IsTwoStageScalarDQACostCtxt(pcc))
			{
				return true;
			}
		}
	}

	DOUBLE dCostDiff = (Cost().Get() - pcc->Cost().Get());
	if (dCostDiff < 0.0)
	{
		// if current context has a strictly smaller cost, then it is preferred
		return true;
	}

	if (dCostDiff > 0.0)
	{
		// if current context has a strictly larger cost, then it is not preferred
		return false;
	}

	// otherwise, we need to break tie in cost values

	// RULE 1: a partitioned plan is better than a non-partitioned
	// one to optimize CPU consumption
	if (CDistributionSpec::EdptPartitioned == Pdpplan()->Pds()->Edpt() &&
		CDistributionSpec::EdptPartitioned != pcc->Pdpplan()->Pds()->Edpt())
	{
		return true;
	}

	// RULE 2: hashed distribution is preferred to random distribution since
	// it preserves knowledge of hash key
	if (CDistributionSpec::EdtHashed == Pdpplan()->Pds()->Edt() &&
		CDistributionSpec::EdtRandom == pcc->Pdpplan()->Pds()->Edt())
	{
		return true;
	}

	// RULE 3: break ties in cost of join plans,
	// if both plans have the same estimated rows for both children, prefer
	// the plan with deeper outer child
	if (CUtils::FPhysicalJoin(Pgexpr()->Pop()) &&
		CUtils::FPhysicalJoin(pcc->Pgexpr()->Pop()))
	{
		CONST_COSTCTXT_PTR pccPrefered = nullptr;
		BOOL fSuccess = false;
		BreakCostTiesForJoinPlans(this, pcc, &pccPrefered, &fSuccess);
		if (fSuccess)
		{
			return (this == pccPrefered);
		}
	}

	if (COperator::EopPhysicalSpool == pcc->Pgexpr()->Pop()->Eopid() &&
		COperator::EopPhysicalSpool == Pgexpr()->Pop()->Eopid())
	{
		CPhysicalSpool *current_best_ctxt =
			CPhysicalSpool::PopConvert(Pgexpr()->Pop());
		CPhysicalSpool *new_ctxt =
			CPhysicalSpool::PopConvert(pcc->Pgexpr()->Pop());

		// if the request does not need to be conscious of motion, then always prefer a
		// streaming spool since a blocking one will be unnecessary
		if (!pcc->Poc()->Prpp()->Per()->PrsRequired()->HasMotionHazard())
		{
			if (new_ctxt->FEager() && !current_best_ctxt->FEager())
			{
				return true;
			}
		}
	}

	return false;
}

BOOL
CCostContext::IsTwoStageScalarDQACostCtxt(const CCostContext *pcc)
{
	if (CUtils::FPhysicalAgg(pcc->Pgexpr()->Pop()))
	{
		CPhysicalAgg *popAgg = CPhysicalAgg::PopConvert(pcc->Pgexpr()->Pop());
		// 2 stage scalar agg are only generated by split dqa xform
		GPOS_ASSERT_IMP(popAgg->IsTwoStageScalarDQA(),
						popAgg->IsAggFromSplitDQA());
		return (popAgg->IsAggFromSplitDQA() && popAgg->IsTwoStageScalarDQA());
	}

	return false;
}

BOOL
CCostContext::IsThreeStageScalarDQACostCtxt(const CCostContext *pcc)
{
	if (CUtils::FPhysicalAgg(pcc->Pgexpr()->Pop()))
	{
		CPhysicalAgg *popAgg = CPhysicalAgg::PopConvert(pcc->Pgexpr()->Pop());
		// 3 stage scalar agg are only generated by split dqa xform
		GPOS_ASSERT_IMP(popAgg->IsThreeStageScalarDQA(),
						popAgg->IsAggFromSplitDQA());
		return (popAgg->IsAggFromSplitDQA() && popAgg->IsThreeStageScalarDQA());
	}

	return false;
}

//---------------------------------------------------------------------------
//	@function:
//		CCostContext::CostCompute
//
//	@doc:
//		Compute cost of current context,
//
//		the function extracts cardinality and row width of owner operator
//		and child operators, and then adjusts row estimate obtained from
//		statistics based on data distribution obtained from plan properties,
//
//		statistics row estimate is computed on logical expressions by
//		estimating the size of the whole relation regardless data
//		distribution, on the other hand, optimizer's cost model computes
//		the cost of a plan instance on some segment,
//
//		when a plan produces tuples distributed to multiple segments, we
//		need to divide statistics row estimate by the number segments to
//		provide a per-segment row estimate for cost computation,
//
//		Note that this scaling of row estimate cannot happen during
//		statistics derivation since plans are not created yet at this point
//
// 		this function also extracts number of rebinds of owner operator child
//		operators, if statistics are computed using predicates with external
//		parameters (outer references), number of rebinds is the total number
//		of external parameters' values
//
//---------------------------------------------------------------------------
CCost
CCostContext::CostCompute(CMemoryPool *mp, CCostArray *pdrgpcostChildren)
{
	// derive context stats
	DeriveStats();

	ULONG arity = 0;
	if (nullptr != m_pdrgpoc)
	{
		arity = Pdrgpoc()->Size();
	}

	m_pstats->AddRef();
	ICostModel::SCostingInfo ci(
		mp, arity, GPOS_NEW(mp) ICostModel::CCostingStats(m_pstats));

	ICostModel *pcm = COptCtxt::PoctxtFromTLS()->GetCostModel();

	CExpressionHandle exprhdl(mp);
	exprhdl.Attach(this);

	// extract local costing info
	DOUBLE rows = m_pstats->Rows().Get();
	if (CDistributionSpec::EdptPartitioned == Pdpplan()->Pds()->Edpt())
	{
		// scale statistics row estimate by number of segments
		rows = DRowsPerHost().Get();
	}
	ci.SetRows(rows);

	DOUBLE width = m_pstats->Width(mp, m_poc->Prpp()->PcrsRequired()).Get();
	ci.SetWidth(width);

	DOUBLE num_rebinds = m_pstats->NumRebinds().Get();
	ci.SetRebinds(num_rebinds);
	GPOS_ASSERT_IMP(
		!exprhdl.HasOuterRefs(),
		GPOPT_DEFAULT_REBINDS == (ULONG)(num_rebinds) &&
			"invalid number of rebinds when there are no outer references");

	// extract children costing info
	for (ULONG ul = 0; ul < arity; ul++)
	{
		COptimizationContext *pocChild = (*m_pdrgpoc)[ul];
		CCostContext *pccChild = pocChild->PccBest();
		GPOS_ASSERT(nullptr != pccChild);

		IStatistics *child_stats = pccChild->Pstats();

		child_stats->AddRef();
		ci.SetChildStats(ul,
						 GPOS_NEW(mp) ICostModel::CCostingStats(child_stats));

		DOUBLE dRowsChild = child_stats->Rows().Get();
		if (CDistributionSpec::EdptPartitioned ==
			pccChild->Pdpplan()->Pds()->Edpt())
		{
			// scale statistics row estimate by number of segments
			dRowsChild = pccChild->DRowsPerHost().Get();
		}
		ci.SetChildRows(ul, dRowsChild);

		DOUBLE dWidthChild =
			child_stats->Width(mp, pocChild->Prpp()->PcrsRequired()).Get();
		ci.SetChildWidth(ul, dWidthChild);

		DOUBLE dRebindsChild = child_stats->NumRebinds().Get();
		ci.SetChildRebinds(ul, dRebindsChild);
		GPOS_ASSERT_IMP(
			!exprhdl.HasOuterRefs(ul),
			GPOPT_DEFAULT_REBINDS == (ULONG)(dRebindsChild) &&
				"invalid number of rebinds when there are no outer references");

		DOUBLE dCostChild = (*pdrgpcostChildren)[ul]->Get();
		ci.SetChildCost(ul, dCostChild);
	}

	// compute cost using the underlying cost model
	return pcm->Cost(exprhdl, &ci);
}

//---------------------------------------------------------------------------
//	@function:
//		CCostContext::DRowsPerHost
//
//	@doc:
//		Return the number of rows per host
//
//---------------------------------------------------------------------------
CDouble
CCostContext::DRowsPerHost() const
{
	DOUBLE rows = Pstats()->Rows().Get();
	COptCtxt *poptctxt = COptCtxt::PoctxtFromTLS();
	const ULONG ulHosts = poptctxt->GetCostModel()->UlHosts();

	CDistributionSpec *pds = Pdpplan()->Pds();
	if (CDistributionSpec::EdtHashed == pds->Edt())
	{
		CDistributionSpecHashed *pdshashed =
			CDistributionSpecHashed::PdsConvert(pds);
		CExpressionArray *pdrgpexpr = pdshashed->Pdrgpexpr();
		CColRefSet *pcrsUsed = CUtils::PcrsExtractColumns(m_mp, pdrgpexpr);

		const CColRefSet *pcrsReqdStats =
			this->Poc()->GetReqdRelationalProps()->PcrsStat();
		if (!pcrsReqdStats->ContainsAll(pcrsUsed))
		{
			// statistics not available for distribution columns, therefore
			// assume uniform distribution across hosts
			// clean up
			pcrsUsed->Release();

			return CDouble(rows / ulHosts);
		}

		ULongPtrArray *pdrgpul = GPOS_NEW(m_mp) ULongPtrArray(m_mp);
		pcrsUsed->ExtractColIds(m_mp, pdrgpul);
		pcrsUsed->Release();

		CStatisticsConfig *stats_config =
			poptctxt->GetOptimizerConfig()->GetStatsConf();
		CDouble dNDVs = CStatisticsUtils::Groups(m_mp, Pstats(), stats_config,
												 pdrgpul, nullptr /*keys*/);
		pdrgpul->Release();

		if (dNDVs < ulHosts)
		{
			// estimated number of distinct values of distribution columns is smaller than number of hosts.
			// We assume data is distributed across a subset of hosts in this case. This results in a larger
			// number of rows per host compared to the uniform case, allowing us to capture data skew in
			// cost computation
			return CDouble(rows / dNDVs.Get());
		}
	}

	return CDouble(rows / ulHosts);
}


//---------------------------------------------------------------------------
//	@function:
//		CCostContext::OsPrint
//
//	@doc:
//		Print function;
//
//---------------------------------------------------------------------------
IOstream &
CCostContext::OsPrint(IOstream &os) const
{
	os << "main ctxt (stage " << m_poc->UlSearchStageIndex() << ")"
	   << m_poc->Id() << "." << m_ulOptReq;

	if (nullptr != m_pdrgpoc)
	{
		os << ", child ctxts:[";
		ULONG arity = m_pdrgpoc->Size();
		if (0 < arity)
		{
			for (ULONG i = 0; i < arity - 1; i++)
			{
				os << (*m_pdrgpoc)[i]->Id();
				os << ", ";
			}
			os << (*m_pdrgpoc)[arity - 1]->Id();
		}
		os << "]";
	}

	if (nullptr != m_pstats)
	{
		os << ", rows:" << m_pstats->Rows();
		if (FOwnsStats())
		{
			os << " (owned)";
		}
		else
		{
			os << " (group)";
		}
	}

	if (m_fPruned)
	{
		os << ", cost lower bound: " << this->Cost() << "\t PRUNED";
	}
	else
	{
		os << ", cost: " << this->Cost();
	}

	return os << std::endl;
}

// EOF

相关信息

greenplumn 源码目录

相关文章

greenplumn CAutoOptCtxt 源码

greenplumn CCTEInfo 源码

greenplumn CCTEMap 源码

greenplumn CCTEReq 源码

greenplumn CCastUtils 源码

greenplumn CColConstraintsArrayMapper 源码

greenplumn CColConstraintsHashMapper 源码

greenplumn CColRef 源码

greenplumn CColRefComputed 源码

greenplumn CColRefSet 源码

0  赞