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 CJobGroupExploration 源码
greenplumn CJobGroupExpression 源码