greenplumn CEngine 源码
greenplumn CEngine 代码
文件路径:/src/backend/gporca/libgpopt/src/engine/CEngine.cpp
//---------------------------------------------------------------------------
// Greenplum Database
// Copyright (C) 2009 - 2011 EMC Corp.
//
// @filename:
// CEngine.cpp
//
// @doc:
// Implementation of optimization engine
//---------------------------------------------------------------------------
#include "gpopt/engine/CEngine.h"
#include "gpos/base.h"
#include "gpos/common/CAutoTimer.h"
#include "gpos/common/syslibwrapper.h"
#include "gpos/error/CAutoTrace.h"
#include "gpos/io/COstreamString.h"
#include "gpos/memory/CAutoMemoryPool.h"
#include "gpos/string/CWStringDynamic.h"
#include "gpos/task/CAutoTaskProxy.h"
#include "gpos/task/CAutoTraceFlag.h"
#include "gpopt/base/CCostContext.h"
#include "gpopt/base/CDrvdPropCtxtPlan.h"
#include "gpopt/base/CEnfdPartitionPropagation.h"
#include "gpopt/base/COptCtxt.h"
#include "gpopt/base/COptimizationContext.h"
#include "gpopt/base/CQueryContext.h"
#include "gpopt/base/CReqdPropPlan.h"
#include "gpopt/base/CReqdPropRelational.h"
#include "gpopt/engine/CEnumeratorConfig.h"
#include "gpopt/engine/CStatisticsConfig.h"
#include "gpopt/exception.h"
#include "gpopt/minidump/CSerializableStackTrace.h"
#include "gpopt/operators/CExpression.h"
#include "gpopt/operators/CExpressionHandle.h"
#include "gpopt/operators/CLogical.h"
#include "gpopt/operators/CPattern.h"
#include "gpopt/operators/CPatternLeaf.h"
#include "gpopt/operators/CPhysicalAgg.h"
#include "gpopt/operators/CPhysicalMotionGather.h"
#include "gpopt/operators/CPhysicalPartitionSelector.h"
#include "gpopt/operators/CPhysicalSort.h"
#include "gpopt/optimizer/COptimizerConfig.h"
#include "gpopt/search/CBinding.h"
#include "gpopt/search/CGroup.h"
#include "gpopt/search/CGroupExpression.h"
#include "gpopt/search/CGroupProxy.h"
#include "gpopt/search/CJob.h"
#include "gpopt/search/CJobFactory.h"
#include "gpopt/search/CMemo.h"
#include "gpopt/search/CScheduler.h"
#include "gpopt/search/CSchedulerContext.h"
#include "gpopt/xforms/CXformFactory.h"
#include "naucrates/traceflags/traceflags.h"
#define GPOPT_SAMPLING_MAX_ITERS 30
#define GPOPT_JOBS_CAP 5000 // maximum number of initial optimization jobs
#define GPOPT_JOBS_PER_GROUP \
20 // estimated number of needed optimization jobs per memo group
// memory consumption unit in bytes -- currently MB
#define GPOPT_MEM_UNIT (1024 * 1024)
#define GPOPT_MEM_UNIT_NAME "MB"
using namespace gpopt;
FORCE_GENERATE_DBGSTR(CEngine);
//---------------------------------------------------------------------------
// @function:
// CEngine::CEngine
//
// @doc:
// Ctor
//
//---------------------------------------------------------------------------
CEngine::CEngine(CMemoryPool *mp)
: m_mp(mp),
m_pqc(nullptr),
m_search_stage_array(nullptr),
m_ulCurrSearchStage(0),
m_pmemo(nullptr),
m_pexprEnforcerPattern(nullptr),
m_xforms(nullptr),
m_pdrgpulpXformCalls(nullptr),
m_pdrgpulpXformTimes(nullptr),
m_pdrgpulpXformBindings(nullptr),
m_pdrgpulpXformResults(nullptr)
{
m_pmemo = GPOS_NEW(mp) CMemo(mp);
m_pexprEnforcerPattern =
GPOS_NEW(mp) CExpression(mp, GPOS_NEW(mp) CPatternLeaf(mp));
m_xforms = GPOS_NEW(mp) CXformSet(mp);
m_pdrgpulpXformCalls = GPOS_NEW(mp) UlongPtrArray(mp);
m_pdrgpulpXformTimes = GPOS_NEW(mp) UlongPtrArray(mp);
m_pdrgpulpXformBindings = GPOS_NEW(mp) UlongPtrArray(mp);
m_pdrgpulpXformResults = GPOS_NEW(mp) UlongPtrArray(mp);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::~CEngine
//
// @doc:
// Dtor
//
//---------------------------------------------------------------------------
CEngine::~CEngine()
{
// in optimized build, we flush-down memory pools without leak checking,
// we can save time in optimized build by skipping all de-allocations here,
// we still have all de-llocations enabled in debug-build to detect any possible leaks
GPOS_DELETE(m_pmemo);
CRefCount::SafeRelease(m_xforms);
m_pdrgpulpXformCalls->Release();
m_pdrgpulpXformTimes->Release();
m_pdrgpulpXformBindings->Release();
m_pdrgpulpXformResults->Release();
m_pexprEnforcerPattern->Release();
CRefCount::SafeRelease(m_search_stage_array);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::InitLogicalExpression
//
// @doc:
// Initialize engine with a given expression
//
//---------------------------------------------------------------------------
void
CEngine::InitLogicalExpression(CExpression *pexpr)
{
GPOS_ASSERT(nullptr == m_pmemo->PgroupRoot() && "Root is already set");
GPOS_ASSERT(pexpr->Pop()->FLogical());
CGroup *pgroupRoot =
PgroupInsert(nullptr /*pgroupTarget*/, pexpr, CXform::ExfInvalid,
nullptr /*pgexprOrigin*/, false /*fIntermediate*/);
m_pmemo->SetRoot(pgroupRoot);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::Init
//
// @doc:
// Initialize engine using a given query context
//
//---------------------------------------------------------------------------
void
CEngine::Init(CQueryContext *pqc, CSearchStageArray *search_stage_array)
{
GPOS_ASSERT(nullptr == m_pqc);
GPOS_ASSERT(nullptr != pqc);
GPOS_ASSERT_IMP(0 == pqc->Pexpr()->DeriveOutputColumns()->Size(),
0 == pqc->Prpp()->PcrsRequired()->Size() &&
"requiring columns from a zero column expression");
m_search_stage_array = search_stage_array;
if (nullptr == search_stage_array)
{
m_search_stage_array = CSearchStage::PdrgpssDefault(m_mp);
}
GPOS_ASSERT(0 < m_search_stage_array->Size());
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
// initialize per-stage xform calls array
const ULONG ulStages = m_search_stage_array->Size();
for (ULONG ul = 0; ul < ulStages; ul++)
{
ULONG_PTR *pulpXformCalls =
GPOS_NEW_ARRAY(m_mp, ULONG_PTR, CXform::ExfSentinel);
ULONG_PTR *pulpXformTimes =
GPOS_NEW_ARRAY(m_mp, ULONG_PTR, CXform::ExfSentinel);
ULONG_PTR *pulpXformBindings =
GPOS_NEW_ARRAY(m_mp, ULONG_PTR, CXform::ExfSentinel);
ULONG_PTR *pulpXformResults =
GPOS_NEW_ARRAY(m_mp, ULONG_PTR, CXform::ExfSentinel);
for (ULONG ulXform = 0; ulXform < CXform::ExfSentinel; ulXform++)
{
pulpXformCalls[ulXform] = 0;
pulpXformTimes[ulXform] = 0;
pulpXformBindings[ulXform] = 0;
pulpXformResults[ulXform] = 0;
}
m_pdrgpulpXformCalls->Append(pulpXformCalls);
m_pdrgpulpXformTimes->Append(pulpXformTimes);
m_pdrgpulpXformBindings->Append(pulpXformBindings);
m_pdrgpulpXformResults->Append(pulpXformResults);
}
}
m_pqc = pqc;
InitLogicalExpression(m_pqc->Pexpr());
m_pqc->PdrgpcrSystemCols()->AddRef();
COptCtxt::PoctxtFromTLS()->SetReqdSystemCols(m_pqc->PdrgpcrSystemCols());
}
//---------------------------------------------------------------------------
// @function:
// CEngine::AddEnforcers
//
// @doc:
// Add enforcers to a memo group
//
//---------------------------------------------------------------------------
void
CEngine::AddEnforcers(
CGroupExpression
*pgexpr, // belongs to group where we need to add enforcers
CExpressionArray *pdrgpexprEnforcers)
{
GPOS_ASSERT(nullptr != pdrgpexprEnforcers);
GPOS_ASSERT(nullptr != pgexpr);
for (ULONG ul = 0; ul < pdrgpexprEnforcers->Size(); ul++)
{
// assemble an expression rooted by the enforcer operator
CExpression *pexprEnforcer = (*pdrgpexprEnforcers)[ul];
#ifdef GPOS_DEBUG
CGroup *pgroup =
#endif // GPOS_DEBUG
PgroupInsert(pgexpr->Pgroup(), pexprEnforcer, CXform::ExfInvalid,
nullptr /*pgexprOrigin*/, false /*fIntermediate*/);
GPOS_ASSERT(pgroup == pgexpr->Pgroup());
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::InsertExpressionChildren
//
// @doc:
// Insert children of the given expression to memo, and copy the groups
// they end up at to the given group array
//
//---------------------------------------------------------------------------
void
CEngine::InsertExpressionChildren(CExpression *pexpr,
CGroupArray *pdrgpgroupChildren,
CXform::EXformId exfidOrigin,
CGroupExpression *pgexprOrigin)
{
GPOS_ASSERT(nullptr != pexpr);
GPOS_ASSERT(nullptr != pdrgpgroupChildren);
ULONG arity = pexpr->Arity();
for (ULONG i = 0; i < arity; i++)
{
CGroup *pgroupChild = nullptr;
COperator *popChild = (*pexpr)[i]->Pop();
if (popChild->FPattern() && CPattern::PopConvert(popChild)->FLeaf())
{
GPOS_ASSERT(nullptr != (*pexpr)[i]->Pgexpr()->Pgroup());
// group is already assigned during binding extraction;
pgroupChild = (*pexpr)[i]->Pgexpr()->Pgroup();
}
else
{
// insert child expression recursively
pgroupChild =
PgroupInsert(nullptr /*pgroupTarget*/, (*pexpr)[i], exfidOrigin,
pgexprOrigin, true /*fIntermediate*/);
}
pdrgpgroupChildren->Append(pgroupChild);
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PgroupInsert
//
// @doc:
// Insert an expression tree into the memo, with explicit target group;
// the function returns a pointer to the group that contains the given
// group expression
//
//---------------------------------------------------------------------------
CGroup *
CEngine::PgroupInsert(CGroup *pgroupTarget, CExpression *pexpr,
CXform::EXformId exfidOrigin,
CGroupExpression *pgexprOrigin, BOOL fIntermediate)
{
// recursive function - check stack
GPOS_CHECK_STACK_SIZE;
GPOS_CHECK_ABORT;
GPOS_ASSERT_IMP(CXform::ExfInvalid != exfidOrigin, nullptr != pgexprOrigin);
CGroup *pgroupOrigin = nullptr;
// check if expression was produced by extracting
// a binding from the memo
if (nullptr != pexpr->Pgexpr())
{
pgroupOrigin = pexpr->Pgexpr()->Pgroup();
GPOS_ASSERT(nullptr != pgroupOrigin && nullptr == pgroupTarget &&
"A valid group is expected");
// if parent has group pointer, all children must have group pointers;
// terminate recursive insertion here
return pgroupOrigin;
}
// if we have a valid origin group, target group must be NULL
GPOS_ASSERT_IMP(nullptr != pgroupOrigin, nullptr == pgroupTarget);
// insert expression's children to memo by recursive call
CGroupArray *pdrgpgroupChildren =
GPOS_NEW(m_mp) CGroupArray(m_mp, pexpr->Arity());
InsertExpressionChildren(pexpr, pdrgpgroupChildren, exfidOrigin,
pgexprOrigin);
COperator *pop = pexpr->Pop();
pop->AddRef();
CGroupExpression *pgexpr = GPOS_NEW(m_mp)
CGroupExpression(m_mp, pop, pdrgpgroupChildren, exfidOrigin,
pgexprOrigin, fIntermediate);
// find the group that contains created group expression
CGroup *pgroupContainer =
m_pmemo->PgroupInsert(pgroupTarget, pexpr, pgexpr);
if (nullptr == pgexpr->Pgroup())
{
// insertion failed, release created group expression
pgexpr->Release();
}
return pgroupContainer;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::InsertXformResult
//
// @doc:
// Insert a set of transformation results to memo
//
//---------------------------------------------------------------------------
void
CEngine::InsertXformResult(
CGroup *pgroupOrigin, CXformResult *pxfres, CXform::EXformId exfidOrigin,
CGroupExpression *pgexprOrigin,
ULONG ulXformTime, // time consumed by transformation in msec
ULONG ulNumberOfBindings)
{
GPOS_ASSERT(nullptr != pxfres);
GPOS_ASSERT(nullptr != pgroupOrigin);
GPOS_ASSERT(CXform::ExfInvalid != exfidOrigin);
GPOS_ASSERT(nullptr != pgexprOrigin);
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics) &&
0 < pxfres->Pdrgpexpr()->Size())
{
(void) m_xforms->ExchangeSet(exfidOrigin);
(*m_pdrgpulpXformCalls)[m_ulCurrSearchStage][exfidOrigin] += 1;
(*m_pdrgpulpXformTimes)[m_ulCurrSearchStage][exfidOrigin] +=
ulXformTime;
(*m_pdrgpulpXformBindings)[m_ulCurrSearchStage][exfidOrigin] +=
ulNumberOfBindings;
(*m_pdrgpulpXformResults)[m_ulCurrSearchStage][exfidOrigin] +=
pxfres->Pdrgpexpr()->Size();
}
CExpression *pexpr = pxfres->PexprNext();
while (nullptr != pexpr)
{
CGroup *pgroupContainer =
PgroupInsert(pgroupOrigin, pexpr, exfidOrigin, pgexprOrigin,
false /*fIntermediate*/);
if (pgroupContainer != pgroupOrigin &&
FPossibleDuplicateGroups(pgroupContainer, pgroupOrigin))
{
gpopt::CMemo::MarkDuplicates(pgroupOrigin, pgroupContainer);
}
pexpr = pxfres->PexprNext();
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FPossibleDuplicateGroups
//
// @doc:
// Check whether the given memo groups can be marked as duplicates. This is
// true only if they have the same logical properties
//
//---------------------------------------------------------------------------
BOOL
CEngine::FPossibleDuplicateGroups(CGroup *pgroupFst, CGroup *pgroupSnd)
{
GPOS_ASSERT(nullptr != pgroupFst);
GPOS_ASSERT(nullptr != pgroupSnd);
CDrvdPropRelational *pdprelFst =
CDrvdPropRelational::GetRelationalProperties(pgroupFst->Pdp());
CDrvdPropRelational *pdprelSnd =
CDrvdPropRelational::GetRelationalProperties(pgroupSnd->Pdp());
// right now we only check the output columns, but we may possibly need to
// check other properties as well
return pdprelFst->GetOutputColumns()->Equals(pdprelSnd->GetOutputColumns());
}
//---------------------------------------------------------------------------
// @function:
// CEngine::DeriveStats
//
// @doc:
// Derive statistics on the root group
//
//---------------------------------------------------------------------------
void
CEngine::DeriveStats(CMemoryPool *pmpLocal)
{
CWStringDynamic str(m_mp);
COstreamString oss(&str);
oss << "\n[OPT]: Statistics Derivation Time (stage " << m_ulCurrSearchStage
<< ") ";
CHAR *sz = CUtils::CreateMultiByteCharStringFromWCString(
m_mp, const_cast<WCHAR *>(str.GetBuffer()));
{
CAutoTimer at(sz, GPOS_FTRACE(EopttracePrintOptimizationStatistics));
// derive stats on root group
CEngine::DeriveStats(pmpLocal, m_mp, PgroupRoot(), nullptr /*prprel*/);
}
GPOS_DELETE_ARRAY(sz);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::DeriveStats
//
// @doc:
// Derive statistics on the group
//
//---------------------------------------------------------------------------
void
CEngine::DeriveStats(CMemoryPool *pmpLocal, CMemoryPool *pmpGlobal,
CGroup *pgroup, CReqdPropRelational *prprel)
{
CGroupExpression *pgexprFirst = CEngine::PgexprFirst(pgroup);
CExpressionHandle exprhdl(pmpGlobal);
exprhdl.Attach(pgexprFirst);
exprhdl.DeriveStats(pmpLocal, pmpGlobal, prprel, nullptr /*stats_ctxt*/);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PgexprFirst
//
// @doc:
// Return the first group expression in a given group
//
//---------------------------------------------------------------------------
CGroupExpression *
CEngine::PgexprFirst(CGroup *pgroup)
{
CGroupExpression *pgexprFirst = nullptr;
{
// group proxy scope
CGroupProxy gp(pgroup);
pgexprFirst = gp.PgexprFirst();
}
GPOS_ASSERT(nullptr != pgexprFirst);
return pgexprFirst;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::EolDamp
//
// @doc:
// Damp optimization level
//
//---------------------------------------------------------------------------
EOptimizationLevel
CEngine::EolDamp(EOptimizationLevel eol)
{
if (EolHigh == eol)
{
return EolLow;
}
return EolSentinel;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FOptimizeChild
//
// @doc:
// Check if parent group expression needs to optimize child group expression.
// This method is called right before a group optimization job is about to
// schedule a group expression optimization job.
//
// Relation properties as well the optimizing parent group expression is
// available to make the decision. So, operators can reject being optimized
// under specific parent operators. For example, a GatherMerge under a Sort
// can be prevented here since it destroys the order from a GatherMerge.
//---------------------------------------------------------------------------
BOOL
CEngine::FOptimizeChild(
CGroupExpression *pgexprParent, CGroupExpression *pgexprChild,
COptimizationContext *pocChild,
EOptimizationLevel eolCurrent // current optimization level in child group
)
{
GPOS_ASSERT(nullptr != PgroupRoot());
GPOS_ASSERT(PgroupRoot()->FImplemented());
GPOS_ASSERT(nullptr != pgexprChild);
GPOS_ASSERT_IMP(nullptr == pgexprParent,
pgexprChild->Pgroup() == PgroupRoot());
if (pgexprParent == pgexprChild)
{
// a group expression cannot optimize itself
return false;
}
if (pgexprChild->Eol() != eolCurrent)
{
// child group expression does not match current optimization level
return false;
}
COperator *popChild = pgexprChild->Pop();
if (nullptr != pgexprParent &&
COperator::EopPhysicalSort == pgexprParent->Pop()->Eopid() &&
COperator::EopPhysicalMotionGather == popChild->Eopid())
{
// prevent (Sort --> GatherMerge), since Sort destroys order maintained by GatherMerge
return !CPhysicalMotionGather::PopConvert(popChild)->FOrderPreserving();
}
return COptimizationContext::FOptimize(m_mp, pgexprParent, pgexprChild,
pocChild, UlSearchStages());
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FSafeToPruneWithDPEStats
//
// @doc:
// Determine if a plan rooted by given group expression can be safely
// pruned during optimization when stats for Dynamic Partition Elimination
// are derived
//
//---------------------------------------------------------------------------
BOOL
CEngine::FSafeToPruneWithDPEStats(CGroupExpression *pgexpr,
CReqdPropPlan *, // prpp
CCostContext *pccChild, ULONG child_index)
{
GPOS_ASSERT(GPOS_FTRACE(EopttraceDeriveStatsForDPE));
GPOS_ASSERT(GPOS_FTRACE(EopttraceEnableSpacePruning));
if (nullptr == pccChild)
{
// group expression has not been optimized yet
CDrvdPropRelational *pdprel =
CDrvdPropRelational::GetRelationalProperties(
pgexpr->Pgroup()->Pdp());
if (0 < pdprel->GetPartitionInfo()->UlConsumers())
{
// we cannot bound cost here because of possible DPE that can happen below the operator
return false;
}
return true;
}
// first child has been optimized
CExpressionHandle exprhdl(m_mp);
exprhdl.Attach(pgexpr);
ULONG ulNextChild = exprhdl.UlNextOptimizedChildIndex(child_index);
CDrvdPropRelational *pdprelChild =
CDrvdPropRelational::GetRelationalProperties(
(*pgexpr)[ulNextChild]->Pdp());
if (0 < pdprelChild->GetPartitionInfo()->UlConsumers())
{
// we cannot bound cost here because of possible DPE that can happen for the unoptimized child
return false;
}
return true;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FSafeToPrune
//
// @doc:
// Determine if a plan rooted by given group expression can be safely
// pruned during optimization
//
//---------------------------------------------------------------------------
BOOL
CEngine::FSafeToPrune(
CGroupExpression *pgexpr, CReqdPropPlan *prpp, CCostContext *pccChild,
ULONG child_index,
CCost *pcostLowerBound // output: a lower bound on plan's cost
)
{
GPOS_ASSERT(nullptr != pcostLowerBound);
*pcostLowerBound = GPOPT_INVALID_COST;
if (!GPOS_FTRACE(EopttraceEnableSpacePruning))
{
// space pruning is disabled
return false;
}
if (GPOS_FTRACE(EopttraceDeriveStatsForDPE) &&
!FSafeToPruneWithDPEStats(pgexpr, prpp, pccChild, child_index))
{
// stat derivation for Dynamic Partition Elimination may not allow non-trivial cost bounds
return false;
}
// check if container group has a plan for given properties
CGroup *pgroup = pgexpr->Pgroup();
COptimizationContext *pocGroup =
pgroup->PocLookupBest(m_mp, UlSearchStages(), prpp);
if (nullptr != pocGroup && nullptr != pocGroup->PccBest())
{
// compute a cost lower bound for the equivalent plan rooted by given group expression
CCost costLowerBound =
pgexpr->CostLowerBound(m_mp, prpp, pccChild, child_index);
*pcostLowerBound = costLowerBound;
if (costLowerBound > pocGroup->PccBest()->Cost())
{
// group expression cannot deliver a better plan for given properties and can be safely pruned
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::Pmemotmap
//
// @doc:
// Build tree map on memo
//
//---------------------------------------------------------------------------
MemoTreeMap *
CEngine::Pmemotmap()
{
COptimizerConfig *optimizer_config =
COptCtxt::PoctxtFromTLS()->GetOptimizerConfig();
if (nullptr == m_pmemo->Pmemotmap())
{
m_pqc->Prpp()->AddRef();
COptimizationContext *poc = GPOS_NEW(m_mp) COptimizationContext(
m_mp, PgroupRoot(), m_pqc->Prpp(),
GPOS_NEW(m_mp) CReqdPropRelational(GPOS_NEW(m_mp) CColRefSet(
m_mp)), // pass empty required relational properties initially
GPOS_NEW(m_mp)
IStatisticsArray(m_mp), // pass empty stats context initially
0 // ulSearchStageIndex
);
m_pmemo->BuildTreeMap(poc);
optimizer_config->GetEnumeratorCfg()->SetPlanSpaceSize(
m_pmemo->Pmemotmap()->UllCount());
poc->Release();
}
return m_pmemo->Pmemotmap();
}
#ifdef GPOS_DEBUG
//---------------------------------------------------------------------------
// @function:
// CEngine::ApplyTransformations
//
// @doc:
// Applies given set of xforms to group expression and insert
// results to memo
//
//---------------------------------------------------------------------------
void
CEngine::ApplyTransformations(CMemoryPool *pmpLocal, CXformSet *xform_set,
CGroupExpression *pgexpr)
{
// iterate over xforms
CXformSetIter xsi(*xform_set);
while (xsi.Advance())
{
GPOS_CHECK_ABORT;
CXform *pxform = CXformFactory::Pxff()->Pxf(xsi.TBit());
// transform group expression, and insert results to memo
CXformResult *pxfres = GPOS_NEW(m_mp) CXformResult(m_mp);
ULONG ulElapsedTime = 0;
ULONG ulNumberOfBindings = 0;
pgexpr->Transform(m_mp, pmpLocal, pxform, pxfres, &ulElapsedTime,
&ulNumberOfBindings);
InsertXformResult(pgexpr->Pgroup(), pxfres, pxform->Exfid(), pgexpr,
ulElapsedTime, ulNumberOfBindings);
pxfres->Release();
if (PssCurrent()->FTimedOut())
{
break;
}
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::TransitionGroupExpression
//
// @doc:
// Transition group expression to a given target state
//
//---------------------------------------------------------------------------
void
CEngine::TransitionGroupExpression(CMemoryPool *pmpLocal,
CGroupExpression *pgexpr,
CGroupExpression::EState estTarget)
{
GPOS_ASSERT(CGroupExpression::estExplored == estTarget ||
CGroupExpression::estImplemented == estTarget);
if (PssCurrent()->FTimedOut())
{
return;
}
CGroupExpression::EState estInitial = CGroupExpression::estExploring;
CGroup::EState estGroupTargetState = CGroup::estExplored;
if (CGroupExpression::estImplemented == estTarget)
{
estInitial = CGroupExpression::estImplementing;
estGroupTargetState = CGroup::estImplemented;
}
pgexpr->SetState(estInitial);
// transition all child groups
ULONG arity = pgexpr->Arity();
for (ULONG i = 0; i < arity; i++)
{
TransitionGroup(pmpLocal, (*pgexpr)[i], estGroupTargetState);
GPOS_CHECK_ABORT;
}
// find which set of xforms should be used
CXformSet *xform_set = CXformFactory::Pxff()->PxfsExploration();
if (CGroupExpression::estImplemented == estTarget)
{
xform_set = CXformFactory::Pxff()->PxfsImplementation();
}
// get all applicable xforms
COperator *pop = pgexpr->Pop();
CXformSet *pxfsCandidates = CLogical::PopConvert(pop)->PxfsCandidates(m_mp);
// intersect them with the required set of xforms, then apply transformations
pxfsCandidates->Intersection(xform_set);
pxfsCandidates->Intersection(PxfsCurrentStage());
ApplyTransformations(pmpLocal, pxfsCandidates, pgexpr);
pxfsCandidates->Release();
pgexpr->SetState(estTarget);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::TransitionGroup
//
// @doc:
// Transition group to a target state
//
//---------------------------------------------------------------------------
void
CEngine::TransitionGroup(CMemoryPool *pmpLocal, CGroup *pgroup,
CGroup::EState estTarget)
{
// check stack size
GPOS_CHECK_STACK_SIZE;
if (PssCurrent()->FTimedOut())
{
return;
}
GPOS_ASSERT(CGroup::estExplored == estTarget ||
CGroup::estImplemented == estTarget);
BOOL fTransitioned = false;
{
CGroupProxy gp(pgroup);
fTransitioned = gp.FTransitioned(estTarget);
}
// check if we can end recursion early
if (!fTransitioned)
{
CGroup::EState estInitial = CGroup::estExploring;
CGroupExpression::EState estGExprTargetState =
CGroupExpression::estExplored;
if (CGroup::estImplemented == estTarget)
{
estInitial = CGroup::estImplementing;
estGExprTargetState = CGroupExpression::estImplemented;
}
CGroupExpression *pgexprCurrent = nullptr;
// transition group's state to initial state
{
CGroupProxy gp(pgroup);
gp.SetState(estInitial);
}
// get first group expression
{
CGroupProxy gp(pgroup);
pgexprCurrent = gp.PgexprFirst();
}
while (nullptr != pgexprCurrent)
{
if (!pgexprCurrent->FTransitioned(estGExprTargetState))
{
TransitionGroupExpression(pmpLocal, pgexprCurrent,
estGExprTargetState);
}
if (PssCurrent()->FTimedOut())
{
break;
}
// get next group expression
{
CGroupProxy gp(pgroup);
pgexprCurrent = gp.PgexprNext(pgexprCurrent);
}
GPOS_CHECK_ABORT;
}
// transition group to target state
{
CGroupProxy gp(pgroup);
gp.SetState(estTarget);
}
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PocChild
//
// @doc:
// Create optimization context for child group
//
//---------------------------------------------------------------------------
COptimizationContext *
CEngine::PocChild(
CGroupExpression *pgexpr, // parent expression
COptimizationContext *pocOrigin, // optimization context of parent operator
CExpressionHandle
&exprhdlPlan, // handle to compute required plan properties
CExpressionHandle
&exprhdlRel, // handle to compute required relational properties
CDrvdPropArray
*pdrgpdpChildren, // derived plan properties of optimized children
IStatisticsArray *pdrgpstatCurrentCtxt, ULONG child_index, ULONG ulOptReq)
{
GPOS_ASSERT(exprhdlPlan.Pgexpr() == pgexpr);
GPOS_ASSERT(nullptr != pocOrigin);
GPOS_ASSERT(nullptr != pdrgpdpChildren);
GPOS_ASSERT(nullptr != pdrgpstatCurrentCtxt);
CGroup *pgroupChild = (*pgexpr)[child_index];
// compute required properties of the n-th child
exprhdlPlan.ComputeChildReqdProps(child_index, pdrgpdpChildren, ulOptReq);
exprhdlPlan.Prpp(child_index)->AddRef();
// use current stats for optimizing current child
IStatisticsArray *stats_ctxt = GPOS_NEW(m_mp) IStatisticsArray(m_mp);
CUtils::AddRefAppend(stats_ctxt, pdrgpstatCurrentCtxt);
// compute required relational properties
CReqdPropRelational *prprel = nullptr;
if (CPhysical::PopConvert(pgexpr->Pop())->FPassThruStats())
{
// copy requirements from origin context
prprel = pocOrigin->GetReqdRelationalProps();
}
else
{
// retrieve requirements from handle
prprel = exprhdlRel.GetReqdRelationalProps(child_index);
}
GPOS_ASSERT(nullptr != prprel);
prprel->AddRef();
COptimizationContext *pocChild = GPOS_NEW(m_mp)
COptimizationContext(m_mp, pgroupChild, exprhdlPlan.Prpp(child_index),
prprel, stats_ctxt, m_ulCurrSearchStage);
return pocChild;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PccOptimizeChild
//
// @doc:
// Optimize child group and return best cost context satisfying
// required properties
//
//---------------------------------------------------------------------------
CCostContext *
CEngine::PccOptimizeChild(
CExpressionHandle &exprhdl, // initialized with required properties
CExpressionHandle &exprhdlRel,
COptimizationContext *pocOrigin, // optimization context of parent operator
CDrvdPropArray *pdrgpdp, IStatisticsArray *pdrgpstatCurrentCtxt,
ULONG child_index, ULONG ulOptReq)
{
CGroupExpression *pgexpr = exprhdl.Pgexpr();
CGroup *pgroupChild = (*exprhdl.Pgexpr())[child_index];
// create optimization context for child group
COptimizationContext *pocChild =
PocChild(pgexpr, pocOrigin, exprhdl, exprhdlRel, pdrgpdp,
pdrgpstatCurrentCtxt, child_index, ulOptReq);
if (pgroupChild == pgexpr->Pgroup() && pocChild->Matches(pocOrigin))
{
// child context is the same as origin context, this is a deadlock
pocChild->Release();
return nullptr;
}
// optimize child group
CGroupExpression *pgexprChildBest =
PgexprOptimize(pgroupChild, pocChild, pgexpr);
pocChild->Release();
if (nullptr == pgexprChildBest || PssCurrent()->FTimedOut())
{
// failed to generate a plan for the child, or search stage is timed-out
return nullptr;
}
// derive plan properties of child group optimal implementation
COptimizationContext *pocFound = pgroupChild->PocLookupBest(
m_mp, m_search_stage_array->Size(), exprhdl.Prpp(child_index));
GPOS_ASSERT(nullptr != pocFound);
CCostContext *pccChildBest = pocFound->PccBest();
GPOS_ASSERT(nullptr != pccChildBest);
// check if optimization can be early terminated after first child has been optimized
CCost costLowerBound(GPOPT_INVALID_COST);
if (exprhdl.UlFirstOptimizedChildIndex() == child_index &&
FSafeToPrune(pgexpr, pocOrigin->Prpp(), pccChildBest, child_index,
&costLowerBound))
{
// failed to optimize child due to cost bounding
(void) pgexpr->PccComputeCost(m_mp, pocOrigin, ulOptReq,
nullptr /*pdrgpoc*/, true /*fPruned*/,
costLowerBound);
return nullptr;
}
return pccChildBest;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PdrgpocOptimizeChildren
//
// @doc:
// Optimize child groups of a given group expression;
//
//---------------------------------------------------------------------------
COptimizationContextArray *
CEngine::PdrgpocOptimizeChildren(
CExpressionHandle &exprhdl, // initialized with required properties
COptimizationContext *pocOrigin, // optimization context of parent operator
ULONG ulOptReq)
{
GPOS_ASSERT(nullptr != exprhdl.Pgexpr());
CGroupExpression *pgexpr = exprhdl.Pgexpr();
const ULONG arity = exprhdl.Arity();
if (0 == arity)
{
// return empty array if no children
return GPOS_NEW(m_mp) COptimizationContextArray(m_mp);
}
// create array of child derived properties
CDrvdPropArray *pdrgpdp = GPOS_NEW(m_mp) CDrvdPropArray(m_mp);
// initialize current stats context with input stats context
IStatisticsArray *pdrgpstatCurrentCtxt =
GPOS_NEW(m_mp) IStatisticsArray(m_mp);
CUtils::AddRefAppend(pdrgpstatCurrentCtxt, pocOrigin->Pdrgpstat());
// initialize required relational properties computation
CExpressionHandle exprhdlRel(m_mp);
CGroupExpression *pgexprForStats =
pgexpr->Pgroup()->PgexprBestPromise(m_mp, pgexpr);
if (nullptr != pgexprForStats)
{
exprhdlRel.Attach(pgexprForStats);
exprhdlRel.DeriveProps(nullptr /*pdpctxt*/);
exprhdlRel.ComputeReqdProps(pocOrigin->GetReqdRelationalProps(),
0 /*ulOptReq*/);
}
// iterate over child groups and optimize them
BOOL fSuccess = true;
ULONG child_index = exprhdl.UlFirstOptimizedChildIndex();
do
{
CGroup *pgroupChild = (*exprhdl.Pgexpr())[child_index];
if (pgroupChild->FScalar())
{
// skip scalar groups from optimization
continue;
}
CCostContext *pccChildBest =
PccOptimizeChild(exprhdl, exprhdlRel, pocOrigin, pdrgpdp,
pdrgpstatCurrentCtxt, child_index, ulOptReq);
if (nullptr == pccChildBest)
{
fSuccess = false;
break;
}
CExpressionHandle exprhdlChild(m_mp);
exprhdlChild.Attach(pccChildBest);
exprhdlChild.DerivePlanPropsForCostContext();
exprhdlChild.Pdp()->AddRef();
pdrgpdp->Append(exprhdlChild.Pdp());
// copy stats of child's best cost context to current stats context
IStatistics *pstat = pccChildBest->Pstats();
pstat->AddRef();
pdrgpstatCurrentCtxt->Append(pstat);
GPOS_CHECK_ABORT;
} while (exprhdl.FNextChildIndex(&child_index));
pdrgpdp->Release();
pdrgpstatCurrentCtxt->Release();
if (!fSuccess)
{
return nullptr;
}
// return child optimization contexts array
return PdrgpocChildren(m_mp, exprhdl);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::OptimizeGroupExpression
//
// @doc:
// Optimize group expression under a given context
//
//---------------------------------------------------------------------------
void
CEngine::OptimizeGroupExpression(CGroupExpression *pgexpr,
COptimizationContext *poc)
{
CGroup *pgroup = pgexpr->Pgroup();
const ULONG ulOptRequests =
CPhysical::PopConvert(pgexpr->Pop())->UlOptRequests();
for (ULONG ul = 0; ul < ulOptRequests; ul++)
{
CExpressionHandle exprhdl(m_mp);
exprhdl.Attach(pgexpr);
exprhdl.DeriveProps(nullptr /*pdpctxt*/);
// check if group expression optimization can be early terminated without optimizing any child
CCost costLowerBound(GPOPT_INVALID_COST);
if (FSafeToPrune(pgexpr, poc->Prpp(), nullptr /*pccChild*/,
gpos::ulong_max /*child_index*/, &costLowerBound))
{
(void) pgexpr->PccComputeCost(m_mp, poc, ul, nullptr /*pdrgpoc*/,
true /*fPruned*/, costLowerBound);
continue;
}
if (FCheckReqdProps(exprhdl, poc->Prpp(), ul))
{
// load required properties on the handle
exprhdl.InitReqdProps(poc->Prpp());
// optimize child groups
COptimizationContextArray *pdrgpoc =
PdrgpocOptimizeChildren(exprhdl, poc, ul);
if (nullptr != pdrgpoc &&
FCheckEnfdProps(m_mp, pgexpr, poc, ul, pdrgpoc))
{
// compute group expression cost under the current optimization context
CCostContext *pccComputed = pgexpr->PccComputeCost(
m_mp, poc, ul, pdrgpoc, false /*fPruned*/, CCost(0.0));
if (nullptr != pccComputed)
{
// update best group expression under the current optimization context
pgroup->UpdateBestCost(poc, pccComputed);
}
}
CRefCount::SafeRelease(pdrgpoc);
}
GPOS_CHECK_ABORT;
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PgexprOptimize
//
// @doc:
// Optimize group under a given context;
//
//---------------------------------------------------------------------------
CGroupExpression *
CEngine::PgexprOptimize(CGroup *pgroup, COptimizationContext *poc,
CGroupExpression *pgexprOrigin)
{
// recursive function - check stack
GPOS_CHECK_STACK_SIZE;
COptimizationContext *pocFound = pgroup->PocInsert(poc);
if (poc != pocFound)
{
GPOS_ASSERT(COptimizationContext::estOptimized == pocFound->Est());
return pocFound->PgexprBest();
}
// add-ref context to pin it in hash table
poc->AddRef();
poc->SetState(COptimizationContext::estOptimizing);
EOptimizationLevel eolCurrent = pgroup->EolMax();
while (EolSentinel != eolCurrent)
{
CGroupExpression *pgexprCurrent = nullptr;
{
CGroupProxy gp(pgroup);
pgexprCurrent = gp.PgexprSkipLogical(nullptr /*pgexpr*/);
}
while (nullptr != pgexprCurrent)
{
if (FOptimizeChild(pgexprOrigin, pgexprCurrent, poc, eolCurrent))
{
OptimizeGroupExpression(pgexprCurrent, poc);
}
if (PssCurrent()->FTimedOut())
{
break;
}
// move to next group expression
{
CGroupProxy gp(pgroup);
pgexprCurrent = gp.PgexprSkipLogical(pgexprCurrent);
}
}
// damp optimization level
eolCurrent = EolDamp(eolCurrent);
GPOS_CHECK_ABORT;
}
poc->SetState(COptimizationContext::estOptimized);
return poc->PgexprBest();
}
//---------------------------------------------------------------------------
// @function:
// CEngine::Explore
//
// @doc:
// Apply all exploration xforms
//
//---------------------------------------------------------------------------
void
CEngine::Explore()
{
GPOS_ASSERT(nullptr != m_pqc);
GPOS_ASSERT(nullptr != PgroupRoot());
// explore root group
GPOS_ASSERT(!PgroupRoot()->FExplored());
TransitionGroup(m_mp, PgroupRoot(), CGroup::estExplored /*estTarget*/);
GPOS_ASSERT_IMP(!PssCurrent()->FTimedOut(), PgroupRoot()->FExplored());
}
//---------------------------------------------------------------------------
// @function:
// CEngine::Implement
//
// @doc:
// Apply all implementation xforms
//
//---------------------------------------------------------------------------
void
CEngine::Implement()
{
GPOS_ASSERT(nullptr != m_pqc);
GPOS_ASSERT(nullptr != PgroupRoot());
// implement root group
GPOS_ASSERT(!PgroupRoot()->FImplemented());
TransitionGroup(m_mp, PgroupRoot(), CGroup::estImplemented /*estTarget*/);
GPOS_ASSERT_IMP(!PssCurrent()->FTimedOut(), PgroupRoot()->FImplemented());
}
//---------------------------------------------------------------------------
// @function:
// CEngine::RecursiveOptimize
//
// @doc:
// Recursive optimization
//
//---------------------------------------------------------------------------
void
CEngine::RecursiveOptimize()
{
CAutoTimer at("\n[OPT]: Total Optimization Time",
GPOS_FTRACE(EopttracePrintOptimizationStatistics));
const ULONG ulSearchStages = m_search_stage_array->Size();
for (ULONG ul = 0; !FSearchTerminated() && ul < ulSearchStages; ul++)
{
PssCurrent()->RestartTimer();
// apply exploration xforms
Explore();
// run exploration completion operations
FinalizeExploration();
// apply implementation xforms
Implement();
// run implementation completion operations
FinalizeImplementation();
// optimize root group
m_pqc->Prpp()->AddRef();
COptimizationContext *poc = GPOS_NEW(m_mp) COptimizationContext(
m_mp, PgroupRoot(), m_pqc->Prpp(),
GPOS_NEW(m_mp) CReqdPropRelational(GPOS_NEW(m_mp) CColRefSet(
m_mp)), // pass empty required relational properties initially
GPOS_NEW(m_mp) IStatisticsArray(
m_mp), // pass an empty stats context initially
m_ulCurrSearchStage);
(void) PgexprOptimize(PgroupRoot(), poc, nullptr /*pgexprOrigin*/);
poc->Release();
// extract best plan found at the end of current search stage
CExpression *pexprPlan = m_pmemo->PexprExtractPlan(
m_mp, m_pmemo->PgroupRoot(), m_pqc->Prpp(),
m_search_stage_array->Size());
PssCurrent()->SetBestExpr(pexprPlan);
FinalizeSearchStage();
}
{
CAutoTrace atSearch(m_mp);
atSearch.Os() << "[OPT]: Search terminated at stage "
<< m_ulCurrSearchStage << "/"
<< m_search_stage_array->Size();
}
if (CEnumeratorConfig::FSample())
{
SamplePlans();
}
}
void
CEngine::DbgPrintExpr(int group_no, int context_no)
{
CAutoTrace at(m_mp);
GPOS_TRY
{
CGroup *top_group = m_pmemo->Pgroup(group_no);
if (nullptr != top_group)
{
COptimizationContext *poc = top_group->Ppoc(context_no);
if (nullptr != poc)
{
CExpression *extracted_expr = m_pmemo->PexprExtractPlan(
m_mp, top_group, poc->Prpp(), m_search_stage_array->Size());
extracted_expr->OsPrint(at.Os());
extracted_expr->Release();
}
else
{
at.Os() << "error: invalid context number";
}
}
else
{
at.Os() << "error: invalid group number";
}
}
GPOS_CATCH_EX(ex)
{
at.Os() << "error, couldn't complete the request";
}
GPOS_CATCH_END;
}
#endif // GPOS_DEBUG
//---------------------------------------------------------------------------
// @function:
// CEngine::PdrgpocChildren
//
// @doc:
// Return array of child optimization contexts corresponding
// to handle requirements
//
//---------------------------------------------------------------------------
COptimizationContextArray *
CEngine::PdrgpocChildren(CMemoryPool *mp, CExpressionHandle &exprhdl)
{
GPOS_ASSERT(nullptr != exprhdl.Pgexpr());
COptimizationContextArray *pdrgpoc =
GPOS_NEW(mp) COptimizationContextArray(mp);
const ULONG arity = exprhdl.Arity();
for (ULONG ul = 0; ul < arity; ul++)
{
CGroup *pgroupChild = (*exprhdl.Pgexpr())[ul];
if (!pgroupChild->FScalar())
{
COptimizationContext *poc = pgroupChild->PocLookupBest(
mp, m_search_stage_array->Size(), exprhdl.Prpp(ul));
GPOS_ASSERT(nullptr != poc);
poc->AddRef();
pdrgpoc->Append(poc);
}
}
return pdrgpoc;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::ScheduleMainJob
//
// @doc:
// Create and schedule the main optimization job
//
//---------------------------------------------------------------------------
void
CEngine::ScheduleMainJob(CSchedulerContext *psc,
COptimizationContext *poc) const
{
GPOS_ASSERT(nullptr != PgroupRoot());
CJobGroupOptimization::ScheduleJob(
psc, PgroupRoot(), nullptr /*pgexprOrigin*/, poc, nullptr /*pjParent*/);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FinalizeExploration
//
// @doc:
// Execute operations after exploration completes
//
//---------------------------------------------------------------------------
void
CEngine::FinalizeExploration()
{
GroupMerge();
if (m_pqc->FDeriveStats())
{
// derive statistics
m_pmemo->ResetStats();
DeriveStats(m_mp);
}
if (!GPOS_FTRACE(EopttraceDonotDeriveStatsForAllGroups))
{
// derive stats for every group without stats
m_pmemo->DeriveStatsIfAbsent(m_mp);
}
if (GPOS_FTRACE(EopttracePrintMemoAfterExploration))
{
{
CAutoTrace at(m_mp);
at.Os() << "MEMO after exploration (stage " << m_ulCurrSearchStage
<< ")" << std::endl;
}
{
CAutoTrace at(m_mp);
at.Os() << *this;
}
}
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
CAutoTrace at(m_mp);
(void) OsPrintMemoryConsumption(
at.Os(), "Memory consumption after exploration ");
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FinalizeImplementation
//
// @doc:
// Execute operations after implementation completes
//
//---------------------------------------------------------------------------
void
CEngine::FinalizeImplementation()
{
if (GPOS_FTRACE(EopttracePrintMemoAfterImplementation))
{
{
CAutoTrace at(m_mp);
at.Os() << "MEMO after implementation (stage "
<< m_ulCurrSearchStage << ")" << std::endl;
}
{
CAutoTrace at(m_mp);
at.Os() << *this;
}
}
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
CAutoTrace at(m_mp);
(void) OsPrintMemoryConsumption(
at.Os(), "Memory consumption after implementation ");
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FinalizeSearchStage
//
// @doc:
// Execute operations after search stage completes
//
//---------------------------------------------------------------------------
void
CEngine::FinalizeSearchStage()
{
ProcessTraceFlags();
m_xforms->Release();
m_xforms = nullptr;
m_xforms = GPOS_NEW(m_mp) CXformSet(m_mp);
m_ulCurrSearchStage++;
m_pmemo->ResetGroupStates();
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PrintActivatedXforms
//
// @doc:
// Print activated xform
//
//---------------------------------------------------------------------------
void
CEngine::PrintActivatedXforms(IOstream &os) const
{
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
os << std::endl
<< "[OPT]: <Begin Xforms - stage " << m_ulCurrSearchStage << ">"
<< std::endl;
CXformSetIter xsi(*m_xforms);
while (xsi.Advance())
{
CXform *pxform = CXformFactory::Pxff()->Pxf(xsi.TBit());
ULONG ulCalls = (ULONG)(
*m_pdrgpulpXformCalls)[m_ulCurrSearchStage][pxform->Exfid()];
ULONG ulTime = (ULONG)(
*m_pdrgpulpXformTimes)[m_ulCurrSearchStage][pxform->Exfid()];
ULONG ulBindings = (ULONG)(
*m_pdrgpulpXformBindings)[m_ulCurrSearchStage][pxform->Exfid()];
ULONG ulResults = (ULONG)(
*m_pdrgpulpXformResults)[m_ulCurrSearchStage][pxform->Exfid()];
os << pxform->SzId() << ": " << ulCalls << " calls, " << ulBindings
<< " total bindings, " << ulResults
<< " alternatives generated, " << ulTime << "ms" << std::endl;
}
os << "[OPT]: <End Xforms - stage " << m_ulCurrSearchStage << ">"
<< std::endl;
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PrintMemoryConsumption
//
// @doc:
// Print current memory consumption
//
//---------------------------------------------------------------------------
IOstream &
CEngine::OsPrintMemoryConsumption(IOstream &os, const CHAR *szHeader) const
{
CMDAccessor *md_accessor = COptCtxt::PoctxtFromTLS()->Pmda();
CMDAccessor::MDCache *pcache = md_accessor->Pcache();
os << std::endl
<< szHeader << "Engine: ["
<< (DOUBLE) m_mp->TotalAllocatedSize() / GPOPT_MEM_UNIT << "] "
<< GPOPT_MEM_UNIT_NAME << ", MD Cache: ["
<< (DOUBLE)(pcache->TotalAllocatedSize()) / GPOPT_MEM_UNIT << "] "
<< GPOPT_MEM_UNIT_NAME << ", Total: ["
<< (DOUBLE)(
CMemoryPoolManager::GetMemoryPoolMgr()->TotalAllocatedSize()) /
GPOPT_MEM_UNIT
<< "] " << GPOPT_MEM_UNIT_NAME;
return os;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::ProcessTraceFlags
//
// @doc:
// Process trace flags after optimization is complete
//
//---------------------------------------------------------------------------
void
CEngine::ProcessTraceFlags()
{
if (GPOS_FTRACE(EopttracePrintMemoAfterOptimization))
{
{
CAutoTrace at(m_mp);
at.Os() << "MEMO after optimization (stage " << m_ulCurrSearchStage
<< "):" << std::endl;
}
{
CAutoTrace at(m_mp);
at.Os() << *this;
}
}
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
CAutoTrace at(m_mp);
// print optimization stats
at.Os() << std::endl
<< "[OPT]: Memo (stage " << m_ulCurrSearchStage << "): ["
<< (ULONG)(m_pmemo->UlpGroups()) << " groups"
<< ", " << m_pmemo->UlDuplicateGroups() << " duplicate groups"
<< ", " << m_pmemo->UlGrpExprs() << " group expressions"
<< ", " << m_xforms->Size() << " activated xforms]";
at.Os() << std::endl
<< "[OPT]: stage " << m_ulCurrSearchStage << " completed in "
<< PssCurrent()->UlElapsedTime() << "ms, ";
if (nullptr == PssCurrent()->PexprBest())
{
at.Os() << " no plan was found";
}
else
{
at.Os() << " plan with cost " << PssCurrent()->CostBest()
<< " was found";
}
PrintActivatedXforms(at.Os());
(void) OsPrintMemoryConsumption(
at.Os(), "Memory consumption after optimization ");
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::Optimize
//
// @doc:
// Main driver of optimization engine
//
//---------------------------------------------------------------------------
void
CEngine::Optimize()
{
CAutoTimer at("\n[OPT]: Total Optimization Time",
GPOS_FTRACE(EopttracePrintOptimizationStatistics));
GPOS_ASSERT(nullptr != PgroupRoot());
GPOS_ASSERT(nullptr != COptCtxt::PoctxtFromTLS());
const ULONG ulJobs =
std::min((ULONG) GPOPT_JOBS_CAP,
(ULONG)(m_pmemo->UlpGroups() * GPOPT_JOBS_PER_GROUP));
CJobFactory jf(m_mp, ulJobs);
CScheduler sched(m_mp, ulJobs);
CSchedulerContext sc;
sc.Init(m_mp, &jf, &sched, this);
const ULONG ulSearchStages = m_search_stage_array->Size();
for (ULONG ul = 0; !FSearchTerminated() && ul < ulSearchStages; ul++)
{
PssCurrent()->RestartTimer();
// optimize root group
m_pqc->Prpp()->AddRef();
COptimizationContext *poc = GPOS_NEW(m_mp) COptimizationContext(
m_mp, PgroupRoot(), m_pqc->Prpp(),
GPOS_NEW(m_mp) CReqdPropRelational(GPOS_NEW(m_mp) CColRefSet(
m_mp)), // pass empty required relational properties initially
GPOS_NEW(m_mp)
IStatisticsArray(m_mp), // pass empty stats context initially
m_ulCurrSearchStage);
// schedule main optimization job
ScheduleMainJob(&sc, poc);
// run optimization job
CScheduler::Run(&sc);
poc->Release();
// extract best plan found at the end of current search stage
CExpression *pexprPlan = m_pmemo->PexprExtractPlan(
m_mp, m_pmemo->PgroupRoot(), m_pqc->Prpp(),
m_search_stage_array->Size());
PssCurrent()->SetBestExpr(pexprPlan);
FinalizeSearchStage();
}
if (GPOS_FTRACE(EopttracePrintOptimizationStatistics))
{
CAutoTrace atSearch(m_mp);
atSearch.Os() << "[OPT]: Search terminated at stage "
<< m_ulCurrSearchStage << "/"
<< m_search_stage_array->Size();
}
if (CEnumeratorConfig::FSample())
{
SamplePlans();
}
}
//---------------------------------------------------------------------------
// @function:
// CEngine::CEngine
//
// @doc:
// Ctor
//
//---------------------------------------------------------------------------
CExpression *
CEngine::PexprUnrank(ULLONG plan_id)
{
// The CTE map will be updated by the Producer instead of the Sequence operator
// because we are doing a DFS traversal of the TreeMap.
CDrvdPropCtxtPlan *pdpctxtplan =
GPOS_NEW(m_mp) CDrvdPropCtxtPlan(m_mp, false /*fUpdateCTEMap*/);
CExpression *pexpr = Pmemotmap()->PrUnrank(m_mp, pdpctxtplan, plan_id);
pdpctxtplan->Release();
#ifdef GPOS_DEBUG
// check plan using configured plan checker, if any
COptimizerConfig *optimizer_config =
COptCtxt::PoctxtFromTLS()->GetOptimizerConfig();
CEnumeratorConfig *pec = optimizer_config->GetEnumeratorCfg();
BOOL fCheck = pec->FCheckPlan(pexpr);
if (!fCheck)
{
CAutoTrace at(m_mp);
at.Os() << "\nextracted plan failed PlanChecker function: " << std::endl
<< *pexpr;
}
GPOS_ASSERT(fCheck);
#endif // GPOS_DEBUG
return pexpr;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PexprExtractPlan
//
// @doc:
// Extract a physical plan from the memo
//
//---------------------------------------------------------------------------
CExpression *
CEngine::PexprExtractPlan()
{
GPOS_ASSERT(nullptr != m_pqc);
GPOS_ASSERT(nullptr != m_pmemo);
GPOS_ASSERT(nullptr != m_pmemo->PgroupRoot());
BOOL fGenerateAlt = false;
COptimizerConfig *optimizer_config =
COptCtxt::PoctxtFromTLS()->GetOptimizerConfig();
CEnumeratorConfig *pec = optimizer_config->GetEnumeratorCfg();
if (gpopt::CEnumeratorConfig::FEnumerate())
{
CAutoTrace at(m_mp);
ULLONG ullCount = Pmemotmap()->UllCount();
at.Os() << "[OPT]: Number of plan alternatives: " << ullCount
<< std::endl;
if (0 < pec->GetPlanId())
{
if (pec->GetPlanId() > ullCount)
{
// an invalid plan number is chosen
GPOS_RAISE(gpopt::ExmaGPOPT, gpopt::ExmiInvalidPlanAlternative,
pec->GetPlanId(), ullCount);
}
// a valid plan number was chosen
fGenerateAlt = true;
}
}
CExpression *pexpr = nullptr;
if (fGenerateAlt)
{
pexpr = PexprUnrank(pec->GetPlanId() -
1 /*rank of plan alternative is zero-based*/);
CAutoTrace at(m_mp);
at.Os() << "[OPT]: Successfully generated plan: " << pec->GetPlanId()
<< std::endl;
}
else
{
pexpr = m_pmemo->PexprExtractPlan(m_mp, m_pmemo->PgroupRoot(),
m_pqc->Prpp(),
m_search_stage_array->Size());
}
if (nullptr == pexpr)
{
GPOS_RAISE(gpopt::ExmaGPOPT, gpopt::ExmiNoPlanFound);
}
// derive plan properties
CDrvdPropCtxtPlan *pdpctxtplan = GPOS_NEW(m_mp) CDrvdPropCtxtPlan(m_mp);
pexpr->PdpDerive(pdpctxtplan);
pdpctxtplan->Release();
return pexpr;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::UllRandomPlanId
//
// @doc:
// Generate random plan id
//
//---------------------------------------------------------------------------
ULLONG
CEngine::UllRandomPlanId(ULONG *seed)
{
ULLONG ullCount = Pmemotmap()->UllCount();
ULLONG plan_id = 0;
do
{
plan_id = clib::Rand(seed);
} while (plan_id >= ullCount);
return plan_id;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FValidPlanSample
//
// @doc:
// Extract a plan sample and handle exceptions according to enumerator
// configurations
//
//---------------------------------------------------------------------------
BOOL
CEngine::FValidPlanSample(CEnumeratorConfig *pec, ULLONG plan_id,
CExpression **ppexpr // output: extracted plan
)
{
GPOS_ASSERT(nullptr != pec);
GPOS_ASSERT(nullptr != ppexpr);
BOOL fValidPlan = true;
if (pec->FSampleValidPlans())
{
// if enumerator is configured to extract valid plans only,
// we extract plan and catch invalid plan exception here
GPOS_TRY
{
*ppexpr = PexprUnrank(plan_id);
}
GPOS_CATCH_EX(ex)
{
if (GPOS_MATCH_EX(ex, gpopt::ExmaGPOPT,
gpopt::ExmiUnsatisfiedRequiredProperties))
{
GPOS_RESET_EX;
fValidPlan = false;
}
else
{
// for all other exceptions, we bail out
GPOS_RETHROW(ex);
}
}
GPOS_CATCH_END;
}
else
{
// otherwise, we extract plan and leave exception handling to the caller
*ppexpr = PexprUnrank(plan_id);
}
return fValidPlan;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::SamplePlans
//
// @doc:
// Sample distribution of possible plans uniformly;
//
//---------------------------------------------------------------------------
void
CEngine::SamplePlans()
{
COptimizerConfig *optimizer_config =
COptCtxt::PoctxtFromTLS()->GetOptimizerConfig();
GPOS_ASSERT(nullptr != optimizer_config);
CEnumeratorConfig *pec = optimizer_config->GetEnumeratorCfg();
ULLONG ullSamples = pec->UllInputSamples();
GPOS_ASSERT(0 < ullSamples);
pec->ClearSamples();
ULLONG ullCount = Pmemotmap()->UllCount();
if (0 == ullCount)
{
// optimizer generated no plans
return;
}
// generate full plan space when space size is less than or equal to
// the required number of samples
BOOL fGenerateAll = (ullSamples >= ullCount);
ULLONG ullTargetSamples = ullSamples;
if (fGenerateAll)
{
ullTargetSamples = ullCount;
}
// find cost of best plan
CExpression *pexpr =
m_pmemo->PexprExtractPlan(m_mp, m_pmemo->PgroupRoot(), m_pqc->Prpp(),
m_search_stage_array->Size());
CCost costBest = pexpr->Cost();
pec->SetBestCost(costBest);
pexpr->Release();
// generate randomized seed using local time
TIMEVAL tv;
syslib::GetTimeOfDay(&tv, nullptr /*timezone*/);
ULONG seed = CombineHashes((ULONG) tv.tv_sec, (ULONG) tv.tv_usec);
// set maximum number of iterations based number of samples
// we use maximum iteration to prevent infinite looping below
const ULLONG ullMaxIters = ullTargetSamples * GPOPT_SAMPLING_MAX_ITERS;
ULLONG ullIters = 0;
ULLONG ull = 0;
while (ullIters < ullMaxIters && ull < ullTargetSamples)
{
// generate id of plan to be extracted
ULLONG plan_id = ull;
if (!fGenerateAll)
{
plan_id = UllRandomPlanId(&seed);
}
pexpr = nullptr;
BOOL fAccept = false;
if (FValidPlanSample(pec, plan_id, &pexpr))
{
// add plan to the sample if it is below cost threshold
CCost cost = pexpr->Cost();
fAccept = pec->FAddSample(plan_id, cost);
pexpr->Release();
}
if (fGenerateAll || fAccept)
{
ull++;
}
ullIters++;
}
pec->PrintPlanSample();
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FCheckEnfdProps
//
// @doc:
// Check enforceable properties and append enforcers to the current group if
// required.
//
// This check is done in two steps:
//
// First, it determines if any particular property needs to be enforced at
// all. For example, the EopttraceDisableSort traceflag can disable order
// enforcement. Also, if there are no partitioned tables referenced in the
// subtree, partition propagation enforcement can be skipped.
//
// Second, EPET methods are called for each property to determine if an
// enforcer needs to be added. These methods in turn call into virtual
// methods in the different operators. For example, CPhysical::EpetOrder()
// is used to determine a Sort node needs to be added to the group. These
// methods are passed an expression handle (to access derived properties of
// the subtree) and the required properties as a object of a subclass of
// CEnfdProp.
//
// Finally, based on return values of the EPET methods,
// CEnfdProp::AppendEnforcers() is called for each of the enforced
// properties.
//
// Returns true if no enforcers were created because they were deemed
// unnecessary or optional i.e all enforced properties were satisfied for
// the group expression under the current optimization context. Returns
// false otherwise.
//
// NB: This method is only concerned with a certain enforcer needs to be
// added into the group. Once added, there is no connection between the
// enforcer and the operator that created it. That is although some group
// expression X created the enforcer E, later, during costing, E can still
// decide to pick some other group expression Y for its child, since
// theoretically, all group expressions in a group are equivalent.
//
//---------------------------------------------------------------------------
BOOL
CEngine::FCheckEnfdProps(CMemoryPool *mp, CGroupExpression *pgexpr,
COptimizationContext *poc, ULONG ulOptReq,
COptimizationContextArray *pdrgpoc)
{
GPOS_CHECK_ABORT;
if (GPOS_FTRACE(EopttracePrintMemoEnforcement))
{
CAutoTrace at(m_mp);
at.Os() << "CEngine::FCheckEnfdProps (Group ID: "
<< pgexpr->Pgroup()->Id() << " Expression ID: " << pgexpr->Id()
<< ")" << std::endl;
m_pmemo->OsPrint(at.Os());
}
// check if all children could be successfully optimized
if (!FChildrenOptimized(pdrgpoc))
{
return false;
}
// load a handle with derived plan properties
poc->AddRef();
pgexpr->AddRef();
pdrgpoc->AddRef();
CCostContext *pcc = GPOS_NEW(mp) CCostContext(mp, poc, ulOptReq, pgexpr);
pcc->SetChildContexts(pdrgpoc);
CExpressionHandle exprhdl(mp);
exprhdl.Attach(pcc);
exprhdl.DerivePlanPropsForCostContext();
CPhysical *popPhysical = CPhysical::PopConvert(exprhdl.Pop());
CReqdPropPlan *prpp = poc->Prpp();
// check whether the current physical operator satisfies the CTE requirements
// and whether it is a motion over unresolved part consumers
if (!popPhysical->FProvidesReqdCTEs(exprhdl, prpp->Pcter()))
{
pcc->Release();
return false;
}
// Determine if any property enforcement is disable or unnecessary
BOOL fOrderReqd = !GPOS_FTRACE(EopttraceDisableSort) &&
!prpp->Peo()->PosRequired()->IsEmpty();
// CPhysicalLeftOuterIndexNLJoin requires the inner child to be any
// distribution but random. The OR makes an exception in this case.
// This should be generalized when more physical operators require
// this pattern. We need an explicit check for CPhysicalLeftOuterIndexNLJoin
// when there are no motions, therefore we need to handle this exceptional
// case here.
//
// Similar exceptions should be OR'd into fDistributionReqdException to
// force checking EpetDistribution on the physical operation
BOOL fDistributionReqdException =
popPhysical->Eopid() == COperator::EopPhysicalLeftOuterIndexNLJoin;
BOOL fDistributionReqd =
!GPOS_FTRACE(EopttraceDisableMotions) &&
((CDistributionSpec::EdtAny != prpp->Ped()->PdsRequired()->Edt()) ||
fDistributionReqdException);
BOOL fRewindabilityReqd = !GPOS_FTRACE(EopttraceDisableSpool) &&
(prpp->Per()->PrsRequired()->IsCheckRequired());
BOOL fPartPropagationReqd =
!GPOS_FTRACE(EopttraceDisablePartPropagation) &&
prpp->Pepp()->PppsRequired()->FPartPropagationReqd();
// Determine if adding an enforcer to the group is required, optional,
// unnecessary or prohibited over the group expression and given the current
// optimization context (required properties)
// get order enforcing type
CEnfdProp::EPropEnforcingType epetOrder =
prpp->Peo()->Epet(exprhdl, popPhysical, fOrderReqd);
// get distribution enforcing type
CEnfdProp::EPropEnforcingType epetDistribution =
prpp->Ped()->Epet(exprhdl, popPhysical, fDistributionReqd);
// get rewindability enforcing type
CEnfdProp::EPropEnforcingType epetRewindability =
prpp->Per()->Epet(exprhdl, popPhysical, fRewindabilityReqd);
// get partition propagation enforcing type
CEnfdProp::EPropEnforcingType epetPartitionPropagation =
prpp->Pepp()->Epet(exprhdl, popPhysical, fPartPropagationReqd);
// Skip adding enforcers entirely if any property determines it to be
// 'prohibited'. In this way, a property may veto out the creation of an
// enforcer for the current group expression and optimization context.
//
// NB: Even though an enforcer E is not added because of some group
// expression G because it was prohibited, some other group expression H may
// decide to add it. And if E is added, it is possible for E to consider both
// G and H as its child.
if (FProhibited(epetOrder, epetDistribution, epetRewindability,
epetPartitionPropagation))
{
pcc->Release();
return false;
}
CExpressionArray *pdrgpexprEnforcers = GPOS_NEW(mp) CExpressionArray(mp);
// extract a leaf pattern from target group
CBinding binding;
CExpression *pexpr =
binding.PexprExtract(m_mp, exprhdl.Pgexpr(), m_pexprEnforcerPattern,
nullptr /* pexprLast */);
GPOS_ASSERT(nullptr != pexpr);
GPOS_ASSERT(pexpr->Pgexpr()->Pgroup() == pgexpr->Pgroup());
prpp->Peo()->AppendEnforcers(mp, prpp, pdrgpexprEnforcers, pexpr, epetOrder,
exprhdl);
prpp->Ped()->AppendEnforcers(mp, prpp, pdrgpexprEnforcers, pexpr,
epetDistribution, exprhdl);
prpp->Per()->AppendEnforcers(mp, prpp, pdrgpexprEnforcers, pexpr,
epetRewindability, exprhdl);
prpp->Pepp()->AppendEnforcers(mp, prpp, pdrgpexprEnforcers, pexpr,
epetPartitionPropagation, exprhdl);
if (0 < pdrgpexprEnforcers->Size())
{
AddEnforcers(exprhdl.Pgexpr(), pdrgpexprEnforcers);
}
pdrgpexprEnforcers->Release();
pexpr->Release();
pcc->Release();
return FOptimize(epetOrder, epetDistribution, epetRewindability,
epetPartitionPropagation);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FChildrenOptimized
//
// @doc:
// Check if all children were successfully optimized
//
//---------------------------------------------------------------------------
BOOL
CEngine::FChildrenOptimized(COptimizationContextArray *pdrgpoc)
{
GPOS_ASSERT(nullptr != pdrgpoc);
const ULONG length = pdrgpoc->Size();
for (ULONG ul = 0; ul < length; ul++)
{
if (nullptr == (*pdrgpoc)[ul]->PgexprBest())
{
return false;
}
}
return true;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FOptimize
//
// @doc:
// Check if optimization is possible under the given property enforcing
// types
//
//---------------------------------------------------------------------------
BOOL
CEngine::FOptimize(CEnfdProp::EPropEnforcingType epetOrder,
CEnfdProp::EPropEnforcingType epetDistribution,
CEnfdProp::EPropEnforcingType epetRewindability,
CEnfdProp::EPropEnforcingType epetPropagation)
{
return CEnfdProp::FOptimize(epetOrder) &&
CEnfdProp::FOptimize(epetDistribution) &&
CEnfdProp::FOptimize(epetRewindability) &&
CEnfdProp::FOptimize(epetPropagation);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FProhibited
//
// @doc:
// Check if any of the given property enforcing types prohibits enforcement
//
//---------------------------------------------------------------------------
BOOL
CEngine::FProhibited(CEnfdProp::EPropEnforcingType epetOrder,
CEnfdProp::EPropEnforcingType epetDistribution,
CEnfdProp::EPropEnforcingType epetRewindability,
CEnfdProp::EPropEnforcingType epetPropagation)
{
return (CEnfdProp::EpetProhibited == epetOrder ||
CEnfdProp::EpetProhibited == epetDistribution ||
CEnfdProp::EpetProhibited == epetRewindability ||
CEnfdProp::EpetProhibited == epetPropagation);
}
//---------------------------------------------------------------------------
// @function:
// CEngine::FCheckReqdProps
//
// @doc:
// Determine if checking required properties is needed.
// This method is called after a group expression optimization job has
// started executing and can be used to cancel the job early.
//
// This is useful to prevent deadlocks when an enforcer optimizes same
// group with the same optimization context. Also, in case the subtree
// doesn't provide the required columns we can save optimization time by
// skipping this optimization request.
//
// NB: Only relational properties are available at this stage to make this
// decision.
//---------------------------------------------------------------------------
BOOL
CEngine::FCheckReqdProps(CExpressionHandle &exprhdl, CReqdPropPlan *prpp,
ULONG ulOptReq)
{
GPOS_CHECK_ABORT;
if (GPOS_FTRACE(EopttracePrintMemoEnforcement))
{
CAutoTrace at(m_mp);
at.Os() << "CEngine::FCheckReqdProps (Group ID: "
<< exprhdl.Pgexpr()->Pgroup()->Id()
<< " Expression ID: " << exprhdl.Pgexpr()->Id() << ")"
<< std::endl;
m_pmemo->OsPrint(at.Os());
}
// check if operator provides required columns
if (!prpp->FProvidesReqdCols(m_mp, exprhdl, ulOptReq))
{
return false;
}
CPhysical *popPhysical = CPhysical::PopConvert(exprhdl.Pop());
COperator::EOperatorId op_id = popPhysical->Eopid();
// check if sort operator is passed an empty order spec;
// this check is required to avoid self-deadlocks, i.e.
// sort optimizing same group with the same optimization context;
BOOL fOrderReqd = !prpp->Peo()->PosRequired()->IsEmpty();
if (!fOrderReqd && COperator::EopPhysicalSort == op_id)
{
return false;
}
// check if motion operator is passed an ANY distribution spec;
// this check is required to avoid self-deadlocks, i.e.
// motion optimizing same group with the same optimization context;
BOOL fDistributionReqd =
(CDistributionSpec::EdtAny != prpp->Ped()->PdsRequired()->Edt());
if (!fDistributionReqd && CUtils::FPhysicalMotion(popPhysical))
{
return false;
}
// check if spool operator is passed a non-rewindable spec;
// this check is required to avoid self-deadlocks, i.e.
// spool optimizing same group with the same optimization context;
if (!prpp->Per()->PrsRequired()->IsCheckRequired() &&
COperator::EopPhysicalSpool == op_id)
{
return false;
}
// check if partition selector is passed a propagation spec not
// involving it's scan-id; this check is required to avoid self-
// deadlocks, i.e partition selector optimizing the same group
// with the same optimization context.
// this also avoids incorrect plans where the partition selector
// is picked on the outer side of a Hash Join, when it requested
// no dynamic partition propagation.
CPartitionPropagationSpec *pps = prpp->Pepp()->PppsRequired();
if (COperator::EopPhysicalPartitionSelector == popPhysical->Eopid())
{
CPhysicalPartitionSelector *part_selector =
CPhysicalPartitionSelector::PopConvert(popPhysical);
if (!pps->Contains(part_selector->ScanId()))
{
return false;
}
}
return true;
}
UlongPtrArray *
CEngine::GetNumberOfBindings()
{
return m_pdrgpulpXformBindings;
}
#ifdef GPOS_DEBUG
//---------------------------------------------------------------------------
// @function:
// CEngine::PrintRoot
//
// @doc:
// Print root group
//
//---------------------------------------------------------------------------
void
CEngine::PrintRoot()
{
CAutoTrace at(m_mp);
at.Os() << "Root Group:" << std::endl;
m_pmemo->Pgroup(m_pmemo->PgroupRoot()->Id())->OsPrint(at.Os());
at.Os() << std::endl;
}
//---------------------------------------------------------------------------
// @function:
// CEngine::PrintOptCtxts
//
// @doc:
// Print main optimization context and optimal cost context
//
//---------------------------------------------------------------------------
void
CEngine::PrintOptCtxts()
{
CAutoTrace at(m_mp);
COptimizationContext *poc = m_pmemo->PgroupRoot()->PocLookupBest(
m_mp, m_search_stage_array->Size(), m_pqc->Prpp());
GPOS_ASSERT(nullptr != poc);
at.Os() << std::endl << "Main Opt Ctxt:" << std::endl;
(void) poc->OsPrintWithPrefix(at.Os(), " ");
at.Os() << std::endl;
at.Os() << "Optimal Cost Ctxt:" << std::endl;
(void) poc->PccBest()->OsPrint(at.Os());
at.Os() << std::endl;
}
#endif // GPOS_DEBUG
//---------------------------------------------------------------------------
// @function:
// CEngine::OsPrint
//
// @doc:
// print function
//
//---------------------------------------------------------------------------
IOstream &
CEngine::OsPrint(IOstream &os) const
{
return m_pmemo->OsPrint(os);
}
// EOF
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦