GPDBandPostgreSQLPortal数据库内核0.0简介Portal(传送门)又称策略选择模块,根据SQL语句的类型选择不同的执行模块(ProcessUtility、Executor)。SQL语句的类型包括:可优化语句和数据定义语句。可优化语句包括DML,如insert/update/select语句,其特点是查询满足条件的元组,并在元组操作后返回给用户或写入磁盘。之所以称为可优化语句,是因为此类语句通常会被优化器重写和优化,以加快查询速度。数据定义语句主要是函数语句,如:DDL(Create,Alter,Drop),DCL(Grant,COMMIT,ROLLBACK)等1.Portal1.1入口层QD执行会从exec_simple_query进入,QE执行会进入来自exec_mpp_query。1.2Portal层1.2.1初识Portal首先认识Portal的内部数据结构:策略只包含一个SELECT查询。从t1中选择*;包含带有RETURNING条件的INSERT/UPDATE/DELETE查询。INSERTINTOret_tbl(id)VALUES(3)RETURNINGidINTOtableId;包含带有修改CTE的SELECT查询。WITHinsAS(INSERTINTOt1(t1_id)VALUES(1)RETURNINGt1_id)SELECT*fromins;例如:下面不是PORTAL_ONE_MOD_WITH,而是PORTAL_MULTI_QUERY。WITHinsAS(SELECT*fromt1)INSERTINTOt2(t2_id,col2)SELECT*fromins;包含一条实用语句,执行这条语句会返回类似SELECT的输出结果。postgres=#explainselect*fromt1;查询计划------------------------------------------------------------------------------聚集运动3:1(切片1;分段:3)(成本=0.00..431.00rows=1width=12)->t1上的SeqScan(cost=0.00..431.00rows=1width=12)Optimizer:PivotalOptimizer(GPORCA)(3rows)postgres=#EXECUTEt1_fn_ret(2,'helloworld');t1_id|col1------+------------2|helloworld(1row)INSERT01othercases,forexample:PORTAL_MULTI_QUERY+PORTAL_MULTI_QUERYPREPAREt1_fn(int,text)ASINSERTINTOt1VALUES($1,$2);PORTAL_MULTI_QUERYPORTAL_UTIL_SELECTPORTAL_ONE_MOD_WITHPORTAL_ONE_RETURNINGPORTAL_ONE_SELECT状态PORTAL_NEWPORTAL_DEFINEDPORTAL_READYPORTAL_QUEUEPORTAL_ACTIVEPORTAL_DONEPORTAL_FAILED这几个状态会在下面依次引入。1.2.2CreatePortal创建一个新的Portal,传入参数为:CreatePortal("",true,true),表示创建一个匿名Portal,允许重复,重复后保持静默。CreatePortal逻辑:PortalCreatePortal(constchar*name,boolallowDup,booldupSilent)根据传入的第一个参数name从hash表中查找。根据传入的第二个参数allowDup,如果第一步找到,则从hash表中查找希腊表决定是否删除。如果为真,则删除,否则报错。当在哈希表中找到Portal且允许重复时,QD节点会根据第三个参数dupSilent决定是否输出告警信息。新建一个Portal,并初始化相应的参数。执行后,将创建一个状态为PORTAL_NEW的Portal。1.2.3PortalDefineQuery定义门户数据,包括:查询语句sourceText、PlannedStmts、查询完成标记qc。注意:nodeTag是根据传入的stmt在QD上设置的,但是在QE上是T_Query,因为在QE上不是解析语句,所以不是T_SelectStmt。最后将Portal状态设置为PORTAL_DEFINED。1.2.4PortalStart准备门户,主要有以下步骤:设置ddesc,信息为QD到QE的附加信息,QD为NULL,QE不为NULL。设置全局参数,例如:当前活动的门户、resourceOwner、context。设置portal参数字段:portalParams,QD上也是NULL,QE上不是NULL。设置门户策略(ChoosePortalStrategy)。如下图所示:输入是一个查询计划链表。对于PORTAL_ONE_SELECT、PORTAL_ONE_MOD_WITH、PORTAL_UTIL_SELECT和PORTAL_ONE_RETURNING,需要一个计划。首先判断是Query还是PlannedStmt。一般来说,查询语句基本都是PlannedStmt。对于像PREPAREst(int)select*fromt1这样的实用语句第一次调用ChoosePortalStrategy返回PORTAL_MULTI_QUERY(命中PlannedStmt),第二次调用返回PORTAL_ONE_SELECT(命中Query)。选择5。根据传送门策略初始化传送门,最重要的是初始化tupDesc和光标位置。例如:“QUERYPLAN”,“t1_id,col1”就是tupDesc。postgres=#explainselect*fromt1;查询计划------------------------------------------------------------------------------聚集运动3:1(切片1;分段:3)(成本=0.00..431.00rows=1width=12)->t1上的SeqScan(cost=0.00..431.00rows=1width=12)Optimizer:PivotalOptimizer(GPORCA)(3rows)postgres=#EXECUTEt1_fn_ret(2,'helloworld');t1_id|col1------+------------2|helloworld(1row)INSERT01不同tupleDesc函数之间的区别ExecTypeFromTLskipresjunkcolumnExecCleanTypeFromTLwithresjunkcolumn6.如果Portal执行过程中出现异常,则设置Portal的状态为PORTAL_FAILED;否则,进行下一步。7.将门户状态设置为PORTAL_READY。1.2.5PortalRun根据SQL语句类型选择不同的执行路径,获取元组数据,完成入口工作。运行后,要么完成要么下一轮(READY,notACTIVE)。portal策略的执行路径如下:PORTAL_ONE_SELECTsetresult=portal->atEndPORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT获取PORTAL_UTIL_SELECT时数据方向包括forward/backward。可以从holdStore获取,也可以从ExectorRun获取填充holdStore(见下)调用PortalRunSelect返回n行数据设置状态为PORTAL_READY,设置是否操作完成,标记为portal->在EndPORTAL_MULTI_QUERY,调用PortalRunMulti,设置状态为PORTAL_Done,设置操作是否完成,标记为true另外,上图中填充holdStore的逻辑如下:调用PortalCreateHoldStore填充portal->holdStore,构造DestReceiver通过工厂函数CreateDestReceiver(子类:TStoreState);根据门户策略执行查询:PORTAL_ONE_RETURNING,PORTAL_ONE_MOD_WITH调用PortalRunMulti,PORTAL_UTIL_SELECT调用PortalRunUtility。PortalRunUtilityPortalRunMultiProcessQueryPortalRunUtilityutilityStmtnotutilityStmtPORTAL_ONE_RETURNING,PORTAL_ONE_MOD_WITHPORTAL_UTIL_SELECT2.CursorCursor2.1开启游标如果不想一下子执行整个命令,可以设置一个封装命令的游标(cursor),然后每次读取几行命令结果。name[[NO]SCROLL]CURSOR[(arguments)]FORquery;例如:DECLAREliahonaSCROLLCURSORFORSELECT*FROMt1;该命令的运行机制是:首先识别为数据定义语句,然后调用ProcessUtility,然后从PlannedStmt中的utilityStmt解析,识别为T_DeclareCursorStmt节点,调用PerformCursorOpen执行声明游标命令。PerformCursorOpen的处理逻辑如下:查询重写优化器优化,生成PlannedStmt创建Portal(名称为游标名),只调用PortalStart2.2关闭游标。关闭游标实际上会关闭Portal并调用PerformPortalClose。下面两个操作:CLOSEcursor_name;关闭所有;如果传入的名称为空,则为CLOSEALL关闭所有不活动的门户,否则,仅关闭指定的门户(游标)。2.3FETCH或MOVEFETCH和MOVE的语法如下:FETCH[direction{FROM|IN}]游标INTO目标;移动[方向{来自|在}]游标;FETCH从游标取n行到target,target可以是一行变量,记录变量,逗号分隔的普通变量列表,和SELECTINTO一样,如果没有取到数据,target会被置为NULL。MOVE重新定位游标而不检索任何数据,例如:一旦确定了游标位置,就可以删除或更新行。MOVEcursor_variable;UPDATEtable_nameSETcolumn=value,...WHERECURRENTOFcursor_variable;从实现层面来说,两者都会进入PerformPortalFetch,都会被解析为FetchStmt。里面有个成员ismove决定是MOVE还是FETCH。无论是哪一个,都会指定游标名称。有了这个名字,门户就知道了,然后调用PortalRunFetch来获取结果。在内部,PortalRunFetch会像PortalRun运行一样,先将入口状态设置为Active,然后根据策略选择不同的调用链。PORTAL_ONE_SELECT调用DoPortalRunFetchPORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT首先判断portal内部是否有holdStore,如果没有则调用FillPortalStore,然后调用DoPortalRunFetch。DoPortalRunFetch内部实现会考虑传入的方向,决定是否向前或向后等不同方向扫描,最后调用PortalRunSelect获取数据。注意:gpdb不支持向后扫描,但是pg支持。
