CJobGroupExpressionExploration类作为Explore group expression optimization job,主要负责创建给定组表达式的逻辑重写。请注意,组探索作业需要为基础组中的每个组表达式运行组表达式探索作业(Responsible for creating the logical rewrites of a given group expression. Note that a group exploration job entails running a group expression exploration job for each group expression in the underlying group)。
首先介绍一点:如图每个 Group【代码中体现为CGroup】 保存的是逻辑等价的GroupExpression【代码中体现为CGroupExpression】;而GroupExpression作为表达式在Cascades中的管理器,真正的优化单元。这里的CJobGroupExpressionExploration就是用于执行GroupExpression Exploration(主要负责推导等价的逻辑表达式)的任务单元。CJobGroupExpressionExploration继承自CJobGroupExpression,而CJobGroupExpression继承自CJob类。
状态流图
CJobGroupExpression使用JSM实现了逻辑状态机,其状态流转如下图所示。由void CJobGroupExpressionExploration::Init(CGroupExpression *pgexpr)
函数对CJobGroupExpression实例进行初始化,首先调用父类CJobGroupExpression::Init(pgexpr)
函数,其实就是设置父类中的m_pgexpr = pgexpr
,以及m_fChildrenScheduled和m_fXformsScheduled为true;调用m_jsm.Init函数初始化逻辑状态机,然后会设置状态机每个状态下执行的函数,如下所示:
// set job actions
m_jsm.SetAction(estInitialized, EevtExploreChildren);
m_jsm.SetAction(estChildrenExplored, EevtExploreSelf);
m_jsm.SetAction(estSelfExplored, EevtFinalize);
ScheduleJob
首先介绍ScheduleJob,该函数是创建调度CJobGroupExpression任务的API(Schedule a new group expression exploration job)。该函数通过JobFactory的PjCreate函数创建CJob::EjtGroupExpressionExploration类型任务,也就是CJobGroupExpression任务,调用Init函数进行初始化,最后通过CScheduler的Add函数建立新建的任务与当前类任务实例的父子关系,并加入到任务等待队列waiting list中。
void CJobGroupExpressionExploration::ScheduleJob(CSchedulerContext *psc, CGroupExpression *pgexpr, CJob *pjParent){
CJob *pj = psc->Pjf()->PjCreate(CJob::EjtGroupExpressionExploration);
// initialize job
CJobGroupExpressionExploration *pjege = PjConvert(pj);
pjege->Init(pgexpr);
psc->Psched()->Add(pjege, pjParent);
}
EevtExploreChildren
首先看在estInitialized状态下,调用的函数EevtExploreChildren。该函数首先看父类CJobGroupExpression的m_fChildrenScheduled是否为true,如果为true说明已经调度过子任务,可以转移到下一个状态;否则,将CGroupExpression m_pgexpr状态设置为CGroupExpression::estExploring,说明正在Exploring,调用ScheduleChildGroupsJobs函数。
CJobGroupExpressionExploration::EEvent CJobGroupExpressionExploration::EevtExploreChildren(CSchedulerContext *psc, CJob *pjOwner) {
CJobGroupExpressionExploration *pjgee = PjConvert(pjOwner); // get a job pointer
if (!pjgee->FChildrenScheduled()) // m_fChildrenScheduled
{
pjgee->m_pgexpr->SetState(CGroupExpression::estExploring);
pjgee->ScheduleChildGroupsJobs(psc);
return eevExploringChildren;
}else{
return eevChildrenExplored;
}
}
ScheduleChildGroupsJobs函数的作用时为所有子groups都调度exploration任务。注意这里的 m_pgexpr->Arity()
代表该表达式关联的子group的数量,而[]号也被CGroupExpression重载过。详细细节可以看CGroupExpression实现。
void CJobGroupExpressionExploration::ScheduleChildGroupsJobs(CSchedulerContext *psc) {
ULONG arity = m_pgexpr->Arity();
for (ULONG i = 0; i < arity; i++)
CJobGroupExploration::ScheduleJob(psc, (*(m_pgexpr))[i], this);
SetChildrenScheduled();
}
从开头的图中我们看到,Group0中的Selection,Join是等价的Group Expression,作为Group0的子Group Expression。因此,每个Group Expression又引用了其他的Group,作为这个表达式的子Group。所以这里的ScheduleChildGroupsJobs会为Group Expression的子groups都调度起CJobGroupExploration任务。
EevtExploreSelf
当子group都Explored之后,进入了estChildrenExplored状态,状态机开始调用EevtExploreSelf函数。首先查看父类的m_fXformsScheduled Xform是否已经应用过了,如果是,进入下一个状态eevSelfExplored;否则需要调用ScheduleApplicableTransformations函数。
CJobGroupExpressionExploration::EEvent CJobGroupExpressionExploration::EevtExploreSelf(CSchedulerContext *psc, CJob *pjOwner){
CJobGroupExpressionExploration *pjgee = PjConvert(pjOwner); // get a job pointer
if (!pjgee->FXformsScheduled()) { // m_fXformsScheduled
pjgee->ScheduleApplicableTransformations(psc);
return eevExploringSelf;
}else{
return eevSelfExplored;
}
}
ScheduleApplicableTransformations函数首先从m_pgexpr中获取到CLogical操作符的规则集,然后拿操作符的规则集和逻辑变换集合取与,得到操作符Exploration阶段需要的规则,然后通过父类的ScheduleTransformations函数使用每个规则对m_pgexpr进行转换。
//---------------------------------------------------------------------------
// @function:
// CJobGroupExpressionExploration::ScheduleApplicableTransformations
// @doc:
// Schedule transformation jobs for all applicable xforms
//---------------------------------------------------------------------------
void CJobGroupExpressionExploration::ScheduleApplicableTransformations( CSchedulerContext *psc) {
// get all applicable xforms
COperator *pop = m_pgexpr->Pop();
CXformSet *xform_set = CLogical::PopConvert(pop)->PxfsCandidates(psc->GetGlobalMemoryPool());
// intersect them with required xforms and schedule jobs
xform_set->Intersection(CXformFactory::Pxff()->PxfsExploration());
xform_set->Intersection(psc->Peng()->PxfsCurrentStage());
ScheduleTransformations(psc, xform_set);
xform_set->Release();
SetXformsScheduled();
}
void CJobGroupExpression::ScheduleTransformations(CSchedulerContext *psc, CXformSet *xform_set){
// iterate on xforms
CXformSetIter xsi(*(xform_set));
while (xsi.Advance()){
CXform *pxform = CXformFactory::Pxff()->Pxf(xsi.TBit());
CJobTransformation::ScheduleJob(psc, m_pgexpr, pxform, this);
}
}
一个表达式,可能对应多个规则。Cascades的设计中,使用集合来管理规则。工程实现中,GPORCA使用位图来管理规则。规则的集合分两类:Exploration Set 用于Exploration的规则;Implementation Set 用于Implementation的规则。
每个操作符,都存有自己需要的规则ID构成的规则的子集,这里面包括了逻辑变换和实现变换的规则。
- Exploration阶段,拿操作符的规则集和逻辑变换集合取与,得到操作符这个阶段需要的规则
- Implementation阶段,拿操作符的规则集和实现变换集合取与,得到操作符这个阶段需要的规则集
GPORCA通过规则工程管理规则集,包括Exploration和Implementation。上图中,0和1有8列,表示规则集。为了简单,我们假设CLogicalGet需要的规则ID是4。LogicalGet所在的行,左边的规则集记录了LogicalGet需要的规则。可以看到第四位被设置为1,可以知道LogicalGet需要ID为4的规则进行转换。Exploration所在的行的规则集,表示所有的规则的集合。5,6,7,8位被设置为1,所以Exploration的规则集是ID为{5,6,7,8}的规则集。同样的,Implementation的规则集为{1,2,3,4}
规则匹配:Exploration优化阶段,LogicalGet和Exploration的规则集取与的结果为空集,所以直接跳过;Implementation阶段,取与的结果是4,是CXformGet2TableScan的ID,所以应用这个规则
EevtFinalize
在EevtFinalize状态下执行的函数EevtFinalize很简单,仅仅是将m_pgexpr状态改为了CGroupExpression::estExplored。
CJobGroupExpressionExploration::EEvent CJobGroupExpressionExploration::EevtFinalize(CSchedulerContext *, CJob *pjOwner){
CJobGroupExpressionExploration *pjgee = PjConvert(pjOwner); // get a job pointer
pjgee->m_pgexpr->SetState(CGroupExpression::estExplored);
return eevFinalized;
}
CJobGroupExpression类
CJobGroupExpression类作为Abstract superclass of all group expression optimization jobs,主要包含3个成员变量,即BOOL m_fChildrenScheduled
、BOOL m_fXformsScheduled
和CGroupExpression *m_pgexpr
。CJobGroupExpression类实际就实现了如下两个函数,其中最重要的是ScheduleTransformations函数,该函数会遍历形参xform_set中的ExformId,并通过CXformFactory获取对应的CXform,最终使用CJobTransformation::ScheduleJob函数创建新Transformation任务。
//---------------------------------------------------------------------------
// @function:
// CJobGroupExpression::Init
// @doc:
// Initialize job
//---------------------------------------------------------------------------
void CJobGroupExpression::Init(CGroupExpression *pgexpr) {
m_fChildrenScheduled = false; m_fXformsScheduled = false;
m_pgexpr = pgexpr;
}
//---------------------------------------------------------------------------
// @function:
// CJobGroupExpression::ScheduleTransformations
// @doc:
// Schedule transformation jobs for the given set of xforms
//---------------------------------------------------------------------------
void CJobGroupExpression::ScheduleTransformations(CSchedulerContext *psc, CXformSet *xform_set){
// iterate on xforms
CXformSetIter xsi(*(xform_set));
while (xsi.Advance()){
CXform *pxform = CXformFactory::Pxff()->Pxf(xsi.TBit());
CJobTransformation::ScheduleJob(psc, m_pgexpr, pxform, this);
}
}
Exploration(主要负责推导等价的逻辑表达式)和Implementation(主要负责把逻辑表达式转化为物理表达式)都是把一个表达式,转换为另一个等价的表达式,统称为Transformation;转换是基于规则进行的,称为基于规则的优化(Role Based Optimization,RBO)。因此CJobGroupExpressionExploration和CJobGroupExpressionImplementation类都会生成CJobTransformation任务。