运维开发网

Postgres中UPDATE语句的源代码分析

运维开发网 https://www.qedev.com 2022-09-12 16:37 出处:网络
这篇文章主要给大家介绍了关于Postgres中UPDATE更新语句源码分析的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

这篇文章主要给大家介绍了关于Postgres中UPDATE更新语句源码分析的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下


PG中UPDATE源码分析

本文主要描述SQL中UPDATE语句的源代码分析,代码为PG13.3版本。


整体流程分析

更新dtea集合id = 1;这个最简单的Update语句分析源代码(dtea不是分区表,不考虑并行性等。,并且不建立任何索引),这有助于我们理解更新的一般过程。

SQL过程如下:

解析器(语法解析,生成语法解析树UpdateStmt,检查语法层是否有错误)

分析(语义分析,UpdateStmt转换成查询树查询,系统表会检查语义错误)

重写(重写规则,根据规则,查询树查询根据预先存储在系统表中的规则重写,否则不重写。另外,视图是根据规则系统实现的,这里也需要处理)

优化器(优化器:逻辑优化、物理优化、执行计划的生成、查询生成的相应执行计划PlannedStmt、基于成本的优化器、最优路径生成的最优执行计划)

Executor (executor,将有各种操作符,根据执行计划、火山模型进行处理,一次一个元组)

存储(存储引擎)。中间也有交易。这里不分析事务处理部分的代码,以免问题复杂化。存储引擎部分不做分析,重点放在三个部分:分析、优化和执行。

对应代码:

exec_simple_query(constchar*query_string)//-------解析器部分----------------gt;pg_parse_query(query_string);//生成语法解析树--gt;pg_analyze_and_rewrite(parsetree,query_string,NULL,0,NULL);//生成查询树Query--gt;parse_analyze(parsetree,query_string,paramTypes,numParams,queryEnv);//语义分析--gt;pg_rewrite_query(query);//规则重写//--------优化器------------gt;pg_plan_queries()//--------执行器------------gt;PortalStart(portal,NULL,0,InvalidSnapshot);--gt;PortalRun(portal,FETCH_ALL,true,true,receiver,receiver,amp;qc);//执行器执行--gt;PortalDrop(portal,false);


解析部分;;生成语法解析树UpdateStmt

关键数据结构:UpdateStmt,RangeVar,ResTarget:

/*UpdateStatement*/typedefstructUpdateStmt{NodeTagtype;RangeVar*relation;/*relationtoupdate*/List*targetList;/*thetargetlist(ofResTarget)*///对应语句中的setid=0;信息在这里Node*whereClause;/*qualifications*/List*fromClause;/*optionalfromclauseformoretables*/List*returningList;/*listofexpressionstoreturn*/WithClause*withClause;/*WITHclause*/}UpdateStmt;//dtea表typedefstructRangeVar{NodeTagtype;char*catalogname;/*thecatalog(database)name,orNULL*/char*schemaname;/*theschemaname,orNULL*/char*relname;/*therelation/sequencename*/boolinh;/*expandrelbyinheritancerecursivelyact*onchildren*/charrelpersistence;/*seeRELPERSISTENCE_*inpg_class.h*/Alias*alias;/*tablealiasamp;optionalcolumnaliases*/intlocation;/*tokenlocation,or-1ifunknown*/}RangeVar;//setid=0;经transformTargetList()-gt;transformTargetEntry,会转为TargetEntrytypedefstructResTarget{NodeTagtype;char*name;/*columnnameorNULL*///idcolumnList*indirection;/*subscripts,fieldnames,and'*',orNIL*/Node*val;/*thevalueexpressiontocomputeorassign*///=1表达式节点存在这里intlocation;/*tokenlocation,or-1ifunknown*/}ResTarget;

用户输入的update语句update dtea set id = 1将从字符串变为数据库可以理解的内部数据结构语法解析树UpdateStmt。逻辑在pg_parse_query(query_string)中执行;在中,你需要了解flex和bison。

gram.y中Update语法的定义:

/******************************************************************************QUERY:*UpdateStmt(UPDATE)*****************************************************************************///结合这条语句分析updatedteasetid=0;UpdateStmt:opt_with_clauseUPDATErelation_expr_opt_aliasSETset_clause_listfrom_clausewhere_or_current_clausereturning_clause{UpdateStmt*n=makeNode(UpdateStmt);n-gt;relation=$3;n-gt;targetList=$5;n-gt;fromClause=$6;n-gt;whereClause=$7;n-gt;returningList=$8;n-gt;withClause=$1;$$=(Node*)n;};set_clause_list:set_clause{$$=$1;}|set_clause_list','set_clause{$$=list_concat($1,$3);};//对应的是setid=0set_clause://id=0set_target'='a_expr{$1-gt;val=(Node*)$3;$$=list_make1($1);}|'('set_target_list')''='a_expr{intncolumns=list_length($2);inti=1;ListCell*col_cell;foreach(col_cell,$2)/*CreateaMultiAssignRefsourceforeachtarget*/{ResTarget*res_col=(ResTarget*)lfirst(col_cell);MultiAssignRef*r=makeNode(MultiAssignRef);r-gt;source=(Node*)$5;r-gt;colno=i;r-gt;ncolumns=ncolumns;res_col-gt;val=(Node*)r;i++;}$$=$2;};set_target:ColIdopt_indirection{$$=makeNode(ResTarget);$$-gt;name=$1;$$-gt;indirection=check_indirection($2,yyscanner);$$-gt;val=NULL;/*upperproductionsetsthis*/$$-gt;location=@1;};set_target_list:set_target{$$=list_make1($1);}|set_target_list','set_target{$$=lappend($1,$3);};


解析部分;;生成查询树Query

UpdateStmt生成后,将通过parse_analyze的语义分析生成查询树查询,后续的优化器可以使用该查询树查询生成执行计划。主代码在src/backent/parser/analyze.c中。

analyze.c:将原始解析树转换成查询树

parse_analyze()--gt;transformTopLevelStmt(pstate,parseTree);--gt;transformOptionalSelectInto(pstate,parseTree-gt;stmt);--gt;transformStmt(pstate,parseTree);//transformsanupdatestatement--gt;transformUpdateStmt(pstate,(UpdateStmt*)parseTree);//实际由UpdateStmt转为Query的处理函数

详细情况我们来看看transformUpdateStmt函数的实现:

/*transformUpdateStmt-transformsanupdatestatement*/staticQuery*transformUpdateStmt(ParseState*pstate,UpdateStmt*stmt){Query*qry=makeNode(Query);ParseNamespaceItem*nsitem;Node*qual;qry-gt;commandType=CMD_UPDATE;pstate-gt;p_is_insert=false;/*processtheWITHclauseindependentlyofallelse*/if(stmt-gt;withClause){qry-gt;hasRecursive=stmt-gt;withClause-gt;recursive;qry-gt;cteList=transformWithClause(pstate,stmt-gt;withClause);qry-gt;hasModifyingCTE=pstate-gt;p_hasModifyingCTE;}qry-gt;resultRelation=setTargetTable(pstate,stmt-gt;relation,stmt-gt;relation-gt;inh,true,ACL_UPDATE);nsitem=pstate-gt;p_target_nsitem;/*subqueriesinFROMcannotaccesstheresultrelation*/nsitem-gt;p_lateral_only=true;nsitem-gt;p_lateral_ok=false;/*theFROMclauseisnon-standardSQLsyntax.WeusedtobeabletodothiswithREPLACEinPOSTQUELsowekeepthefeature.*/transformFromClause(pstate,stmt-gt;fromClause);/*remainingclausescanreferencetheresultrelationnormally*/nsitem-gt;p_lateral_only=false;nsitem-gt;p_lateral_ok=true;qual=transformWhereClause(pstate,stmt-gt;whereClause,EXPR_KIND_WHERE,"WHERE");qry-gt;returningList=transformReturningList(pstate,stmt-gt;returningList);/*NowwearedonewithSELECT-likeprocessing,andcangetonwith*transformingthetargetlisttomatchtheUPDATEtargetcolumns.*/qry-gt;targetList=transformUpdateTargetList(pstate,stmt-gt;targetList);//处理SQL语句中的setid=1qry-gt;rtable=pstate-gt;p_rtable;qry-gt;jointree=makeFromExpr(pstate-gt;p_joinlist,qual);qry-gt;hasTargetSRFs=pstate-gt;p_hasTargetSRFs;qry-gt;hasSubLinks=pstate-gt;p_hasSubLinks;assign_query_collations(pstate,qry);returnqry;}

这里,我们应该关注transformTargetList,它会将抽象语法树中的ResTarget变成查询的TargetEntry。

typedefstructTargetEntry{Exprxpr;Expr*expr;/*expressiontoevaluate*/AttrNumberresno;/*attributenumber(seenotesabove)*/char*resname;/*nameofthecolumn(couldbeNULL)*/Indexressortgroupref;/*nonzeroifreferencedbyasort/groupclause*/Oidresorigtbl;/*OIDofcolumn'ssourcetable*/AttrNumberresorigcol;/*column'snumberinsourcetable*/boolresjunk;/*settotruetoeliminatetheattributefromfinaltargetlist*/}TargetEntry;

其内部处理请参考源代码src/backend/parser中的相关处理,此处不再赘述。你需要专注于阅读自述文件。PG源代码里所有的readmes都是非常好的素材,一定要仔细阅读。


优化器;;生成执行计划

这一节内容很多。主要逻辑是先优化逻辑,比如子查询、子链接、常量表达式、选择下推等。因为我们要分析的语句很简单,逻辑优化这部分就不涉及了。优化涉及选择率、成本估计、索引扫描或顺序扫描、选择哪种连接方法、动态规划或遗传算法、nestloop-join、merge-join或hash-join等。因为我们的表没有索引,更新单个表不涉及多表连接,所以物理优化涉及不多。生成路径,生成最佳路径,然后从最佳路径生成执行计划。

在路径生成部分,最基本的是表的扫描方式,比如顺序扫描和索引扫描,然后是连接方式。有了连接方法,那么排序和限制等路径......,都是自下而上生成的。我们要分析的语句很简单。如果没有其他处理,就按顺序扫描,更新即可。

这里我们不考虑并行执行计划。让我们先来看看其实施计划的结果:

postgres@postgres=#explainupdatedteasetid=0;QUERYPLAN--------------------------------------------------------------Updateondtea(cost=0.00..19.00rows=900width=68)-gt;SeqScanondtea(cost=0.00..19.00rows=900width=68)(2rows)

我们来分析一下其执行计划的生成过程:

//由查询树Query--gt;Path--gt;Plan(PlannedStmt)pg_plan_queries()--gt;pg_plan_query()--gt;planner()--gt;standard_planner(Query*parse,constchar*query_string,intcursorOptions,ParamListInfoboundParams)//由Query---gt;PlannerInfo--gt;subquery_planner(glob,parse,NULL,false,tuple_fraction);//涉及到很多逻辑优化的内容,很多不列出--gt;pull_up_sublinks(root);--gt;pull_up_subqueries(root);//这里只列出几个重要的逻辑优化内容,其他的不再列出......//如果是update/delete分区表继承表则走inheritance_planner(),其他情况走grouping_planner()--gt;inheritance_planner()//update/delete分区表继承表的情况--gt;grouping_planner()--gt;grouping_planner()//非分区表、继承表的情况--gt;preprocess_targetlist(root);//update虽然只更新一列,但是插入一条新元组的时候,需要知道其他列信息.--gt;rewriteTargetListUD(parse,target_rte,target_relation);--gt;expand_targetlist()--gt;query_planner(root,standard_qp_callback,amp;qp_extra);//重要--gt;add_base_rels_to_query()--gt;deconstruct_jointree(root);--gt;add_other_rels_to_query(root);//展开分区表到PlannerInfo中的相关字段中--gt;expand_inherited_rtentry()--gt;expand_planner_arrays(root,num_live_parts);--gt;make_one_rel(root,joinlist);--gt;set_base_rel_sizes(root);--gt;set_rel_size();--gt;set_append_rel_size(root,rel,rti,rte);//如果是分区表或者继承走这里,否则走下面--gt;set_rel_size(root,childrel,childRTindex,childRTE);//处理子分区表--gt;set_plain_rel_size(root,rel,rte);--gt;set_plain_rel_size()//如果不是分区表或者继承--gt;set_baserel_size_estimates()--gt;set_base_rel_pathlists(root);--gt;set_rel_pathlist(root,rel,rti,root-gt;simple_rte_array[rti]);--gt;set_append_rel_pathlist(root,rel,rti,rte);//生成各分区表的访问路径--gt;make_rel_from_joinlist(root,joinlist);//动态规划还是基因规划--gt;standard_join_search()//动态规划--gt;geqo()//基因规划与动态规划二选一--gt;apply_scanjoin_target_to_paths()--gt;create_modifytable_path()//由PlannerInfo---gt;RelOptInfo--gt;fetch_upper_rel(root,UPPERREL_FINAL,NULL);//由RelOptInfo---gt;Path--gt;get_cheapest_fractional_path(final_rel,tuple_fraction);//由PlannerInfo+Path---gt;Plan--gt;create_plan(root,best_path);//后续处理,由Plan---gt;PlannedStmt

核心数据结构:PlannedStmt,PlannerInfo,RelOptInfo(存储访问路径及其开销),Path

Path:所有的路径都是从Path继承的,所以这个比较重要。

typedefstructPath{NodeTagtype;NodeTagpathtype;/*tagidentifyingscan/joinmethod*/RelOptInfo*parent;/*therelationthispathcanbuild*/PathTarget*pathtarget;/*listofVars/Exprs,cost,width*/ParamPathInfo*param_info;/*parameterizationinfo,orNULLifnone*/boolparallel_aware;/*engageparallel-awarelogic*/boolparallel_safe;/*OKtouseaspartofparallelplan*/intparallel_workers;/*desired#ofworkers;0=notparallel*//*estimatedsize/costsforpath(seecostsize.cformoreinfo)*/doublerows;/*estimatednumberofresulttuples*/Coststartup_cost;/*costexpendedbeforefetchinganytuples*/Costtotal_cost;/*totalcost(assumingalltuplesfetched)*/List*pathkeys;/*sortorderingofpath'soutput*//*pathkeysisaListofPathKeynodes;seeabove*/}Path;/*ModifyTablePathrepresentsperformingINSERT/UPDATE/DELETEmodifications*WerepresentmostthingsthatwillbeintheModifyTableplannode*literally,exceptwehavechildPath(s)notPlan(s).Butanalysisofthe*OnConflictExprisdeferredtocreateplan.c,asiscollectionofFDWdata.*/typedefstructModifyTablePath{Pathpath;//可以看到ModifyTablePath继承自PathCmdTypeoperation;/*INSERT,UPDATE,orDELETE*/boolcanSetTag;/*dowesetthecommandtag/es_processed*/IndexnominalRelation;/*ParentRTindexforuseofEXPLAIN*/IndexrootRelation;/*RootRTindex,iftargetispartitioned*/boolpartColsUpdated;/*somepartkeyinhierarchyupdated*/List*resultRelations;/*integerlistofRTindexes*/List*subpaths;/*Path(s)producingsourcedata*/List*subroots;/*per-target-tablePlannerInfos*/List*withCheckOptionLists;/*per-target-tableWCOlists*/List*returningLists;/*per-target-tableRETURNINGtlists*/List*rowMarks;/*PlanRowMarks(non-lockingonly)*/OnConflictExpr*onconflict;/*ONCONFLICTclause,orNULL*/intepqParam;/*IDofParamforEvalPlanQualre-eval*/}ModifyTablePath;

生成更新执行路径,最终生成ModifyTablePath。在本例中,路径生成过程为:Path-gt;投影路径-gt;ModifyTablePath,即在修改表之前顺序扫描表。执行计划由以下路径生成。

/*create_modifytable_path*CreatesapathnodethatrepresentsperformingINSERT/UPDATE/DELETEmods**'rel'istheparentrelationassociatedwiththeresult*'resultRelations'isanintegerlistofactualRTindexesoftargetrel(s)*'subpaths'isalistofPath(s)producingsourcedata(oneperrel)*'subroots'isalistofPlannerInfostructs(oneperrel)*/ModifyTablePath*create_modifytable_path(PlannerInfo*root,RelOptInfo*rel,CmdTypeoperation,boolcanSetTag,IndexnominalRelation,IndexrootRelation,boolpartColsUpdated,List*resultRelations,List*subpaths,List*subroots,List*withCheckOptionLists,List*returningLists,List*rowMarks,OnConflictExpr*onconflict,intepqParam){ModifyTablePath*pathnode=makeNode(ModifyTablePath);doubletotal_size;ListCell*lc;Assert(list_length(resultRelations)==list_length(subpaths));Assert(list_length(resultRelations)==list_length(subroots));Assert(withCheckOptionLists==NIL||list_length(resultRelations)==list_length(withCheckOptionLists));Assert(returningLists==NIL||list_length(resultRelations)==list_length(returningLists));pathnode-gt;path.pathtype=T_ModifyTable;pathnode-gt;path.parent=rel;pathnode-gt;path.pathtarget=rel-gt;reltarget;/*pathtargetisnotinteresting,justmakeitminimallyvalid*//*Fornow,assumeweareaboveanyjoins,sonoparameterization*/pathnode-gt;path.param_info=NULL;pathnode-gt;path.parallel_aware=false;pathnode-gt;path.parallel_safe=false;pathnode-gt;path.parallel_workers=0;pathnode-gt;path.pathkeys=NIL;/**Computecostamp;rowcountassumofsubpathcostsamp;rowcounts.**Currently,wedon'tchargeanythingextrafortheactualtable*modificationwork,norfortheWITHCHECKOPTIONSorRETURNING*expressionsifany.Itwouldonlybewindowdressing,since*ModifyTableisalwaysatop-levelnodeandthereisnowayforthe*coststochangeanyhigher-levelplanningchoices.Butwemightwant*tomakeitlookbettersometime.*/pathnode-gt;path.startup_cost=0;pathnode-gt;path.total_cost=0;pathnode-gt;path.rows=0;total_size=0;foreach(lc,subpaths){Path*subpath=(Path*)lfirst(lc);if(lc==list_head(subpaths))/*firstnode*/pathnode-gt;path.startup_cost=subpath-gt;startup_cost;pathnode-gt;path.total_cost+=subpath-gt;total_cost;pathnode-gt;path.rows+=subpath-gt;rows;total_size+=subpath-gt;pathtarget-gt;width*subpath-gt;rows;}/*Setwidthtotheaveragewidthofthesubpathoutputs.XXXthisis*totallywrong:weshouldreportzeroifnoRETURNING,elseanaverage*oftheRETURNINGtlistwidths.Butit'swhathappenedhistorically,*andimprovingitisataskforanotherday.*/if(pathnode-gt;path.rowsgt;0)total_size/=pathnode-gt;path.rows;pathnode-gt;path.pathtarget-gt;width=rint(total_size);pathnode-gt;operation=operation;pathnode-gt;canSetTag=canSetTag;pathnode-gt;nominalRelation=nominalRelation;pathnode-gt;rootRelation=rootRelation;pathnode-gt;partColsUpdated=partColsUpdated;pathnode-gt;resultRelations=resultRelations;pathnode-gt;subpaths=subpaths;pathnode-gt;subroots=subroots;pathnode-gt;withCheckOptionLists=withCheckOptionLists;pathnode-gt;returningLists=returningLists;pathnode-gt;rowMarks=rowMarks;pathnode-gt;onconflict=onconflict;pathnode-gt;epqParam=epqParam;returnpathnode;}

既然我们已经生成了最佳更新路径,我们需要从该路径生成执行计划:

Plan*create_plan(PlannerInfo*root,Path*best_path){Plan*plan;Assert(root-gt;plan_params==NIL);/*plan_paramsshouldnotbeinuseincurrentquerylevel*//*Initializethismodule'sworkspaceinPlannerInfo*/root-gt;curOuterRels=NULL;root-gt;curOuterParams=NIL;/*Recursivelyprocessthepathtree,demandingthecorrecttlistresult*/plan=create_plan_recurse(root,best_path,CP_EXACT_TLIST);//实际实现是在这里/**Makesurethetopmostplannode'stargetlistexposestheoriginal*columnnamesandotherdecorativeinfo.Targetlistsgeneratedwithin*theplannerdon'tbotherwiththatstuff,butwemusthaveitonthe*top-leveltlistseenatexecutiontime.However,ModifyTableplan*nodesdon'thaveatlistmatchingthequerytreetargetlist.*/if(!IsA(plan,ModifyTable))apply_tlist_labeling(plan-gt;targetlist,root-gt;processed_tlist);/**AttachanyinitPlanscreatedinthisqueryleveltothetopmostplan*node.(Inprincipletheinitplanscouldgoinanyplannodeator*abovewherethey'rereferenced,butthereseemsnoreasontoputthem*anylowerthanthetopmostnodeforthequerylevel.Also,see*commentsforSS_finalize_planbeforeyoutrytochangethis.)*/SS_attach_initplans(root,plan);/*CheckwesuccessfullyassignedallNestLoopParamstoplannodes*/if(root-gt;curOuterParams!=NIL)elog(ERROR,"failedtoassignallNestLoopParamstoplannodes");/**Resetplan_paramstoensureparamIDsusedfornestloopparamsarenotre-usedlater*/root-gt;plan_params=NIL;returnplan;}//由最佳路径生成最佳执行计划staticModifyTable*create_modifytable_plan(PlannerInfo*root,ModifyTablePath*best_path){ModifyTable*plan;List*subplans=NIL;ListCell*subpaths,*subroots;/*Buildtheplanforeachinputpath*/forboth(subpaths,best_path-gt;subpaths,subroots,best_path-gt;subroots){Path*subpath=(Path*)lfirst(subpaths);PlannerInfo*subroot=(PlannerInfo*)lfirst(subroots);Plan*subplan;/*InaninheritedUPDATE/DELETE,referencetheper-childmodified*subrootwhilecreatingPlansfromPathsforthechildrel.Thisis*akluge,butotherwiseit'stoohardtoensurethatPlancreation*functions(particularlyinFDWs)don'tdependonthecontentsof*"root"matchingwhattheysawatPathcreationtime.Themain*downsideisthatcreationfunctionsforPlansthatmightappear*belowaModifyTablecannotexpecttomodifythecontentsof"root"*andhaveit"stick"forsubsequentprocessingsuchassetrefs.c.*That'snotgreat,butitseemsbetterthanthealternative.*/subplan=create_plan_recurse(subroot,subpath,CP_EXACT_TLIST);/*Transferresname/resjunklabeling,too,tokeepexecutorhappy*/apply_tlist_labeling(subplan-gt;targetlist,subroot-gt;processed_tlist);subplans=lappend(subplans,subplan);}plan=make_modifytable(root,best_path-gt;operation,best_path-gt;canSetTag,best_path-gt;nominalRelation,best_path-gt;rootRelation,best_path-gt;partColsUpdated,best_path-gt;resultRelations,subplans,best_path-gt;subroots,best_path-gt;withCheckOptionLists,best_path-gt;returningLists,best_path-gt;rowMarks,best_path-gt;onconflict,best_path-gt;epqParam);copy_generic_path_info(amp;plan-gt;plan,amp;best_path-gt;path);returnplan;}

最终执行计划可修改:

/*----------------*ModifyTablenode-*Applyrowsproducedbysubplan(s)toresulttable(s),*byinserting,updating,ordeleting.**Iftheoriginallynamedtargettableisapartitionedtable,both*nominalRelationandrootRelationcontaintheRTindexofthepartition*root,whichisnototherwisementionedintheplan.OtherwiserootRelation*iszero.However,nominalRelationwillalwaysbeset,asit'stherelthat*EXPLAINshouldclaimistheINSERT/UPDATE/DELETEtarget.**NotethatrowMarksandepqParamarepresumedtobevalidforallthe*subplan(s);theycan'tcontainanyinfothatvariesacrosssubplans.*----------------*/typedefstructModifyTable{Planplan;CmdTypeoperation;/*INSERT,UPDATE,orDELETE*/boolcanSetTag;/*dowesetthecommandtag/es_processed*/IndexnominalRelation;/*ParentRTindexforuseofEXPLAIN*/IndexrootRelation;/*RootRTindex,iftargetispartitioned*/boolpartColsUpdated;/*somepartkeyinhierarchyupdated*/List*resultRelations;/*integerlistofRTindexes*/intresultRelIndex;/*indexoffirstresultRelinplan'slist*/introotResultRelIndex;/*indexofthepartitionedtableroot*/List*plans;/*plan(s)producingsourcedata*/List*withCheckOptionLists;/*per-target-tableWCOlists*/List*returningLists;/*per-target-tableRETURNINGtlists*/List*fdwPrivLists;/*per-target-tableFDWprivatedatalists*/Bitmapset*fdwDirectModifyPlans;/*indicesofFDWDMplans*/List*rowMarks;/*PlanRowMarks(non-lockingonly)*/intepqParam;/*IDofParamforEvalPlanQualre-eval*/OnConflictActiononConflictAction;/*ONCONFLICTaction*/List*arbiterIndexes;/*ListofONCONFLICTarbiterindexOIDs*/List*onConflictSet;/*SETforINSERTONCONFLICTDOUPDATE*/Node*onConflictWhere;/*WHEREforONCONFLICTUPDATE*/IndexexclRelRTI;/*RTIoftheEXCLUDEDpseudorelation*/List*exclRelTlist;/*tlistoftheEXCLUDEDpseudorelation*/}ModifyTable;


执行器

按照上面的执行计划,去执行。主要是各种算子的实现,其中需要了解执行器的工作原理,主要是火山模型,一次一个元组。我们来看看它的调用过程。

CreatePortal("",true,true);PortalDefineQuery(portal,NULL,query_string,commandTag,plantree_list,NULL);PortalStart(portal,NULL,0,InvalidSnapshot);PortalRun(portal,FETCH_ALL,true,true,receiver,receiver,amp;qc);--gt;PortalRunMulti()--gt;ProcessQuery()--gt;ExecutorStart(queryDesc,0);--gt;standard_ExecutorStart()--gt;estate=CreateExecutorState();//创建EState--gt;estate-gt;es_output_cid=GetCurrentCommandId(true);//获得cid,后面更新的时候要用--gt;InitPlan(queryDesc,eflags);--gt;ExecInitNode(plan,estate,eflags);--gt;ExecInitModifyTable()//初始化ModifyTableState--gt;ExecutorRun(queryDesc,ForwardScanDirection,0L,true);--gt;standard_ExecutorRun()--gt;ExecutePlan()--gt;ExecProcNode(planstate);//一次一元组火山模型--gt;node-gt;ExecProcNode(node);--gt;ExecProcNodeFirst(PlanState*node)--gt;node-gt;ExecProcNode(node);--gt;ExecModifyTable(PlanState*pstate)--gt;ExecUpdate()--gt;table_tuple_update(Relationrel,......)--gt;rel-gt;rd_tableam-gt;tuple_update()--gt;heapam_tuple_update(Relationrelation,......)--gt;heap_update(relation,otid,tuple,cid,......)--gt;ExecutorFinish(queryDesc);--gt;ExecutorEnd(queryDesc);PortalDrop(portal,false);

关键数据结构:

//ModifyTableStateinformationtypedefstructModifyTableState{PlanStateps;/*itsfirstfieldisNodeTag*/CmdTypeoperation;/*INSERT,UPDATE,orDELETE*/boolcanSetTag;/*dowesetthecommandtag/es_processed*/boolmt_done;/*arewedone*/PlanState**mt_plans;/*subplans(onepertargetrel)*/intmt_nplans;/*numberofplansinthearray*/intmt_whichplan;/*whichoneisbeingexecuted(0..n-1)*/TupleTableSlot**mt_scans;/*inputtuplecorrespondingtounderlying*plans*/ResultRelInfo*resultRelInfo;/*per-subplantargetrelations*/ResultRelInfo*rootResultRelInfo;/*roottargetrelation(partitioned*tableroot)*/List**mt_arowmarks;/*per-subplanExecAuxRowMarklists*/EPQStatemt_epqstate;/*forevaluatingEvalPlanQualrechecks*/boolfireBSTriggers;/*doweneedtofirestmttriggers*//*Slotforstoringtuplesintherootpartitionedtable'srowtypeduring*anUPDATEofapartitionedtable.*/TupleTableSlot*mt_root_tuple_slot;structPartitionTupleRouting*mt_partition_tuple_routing;/*Tuple-routingsupportinfo*/structTransitionCaptureState*mt_transition_capture;/*controlstransitiontablepopulationforspecifiedoperation*//*controlstransitiontablepopulationforINSERT...ONCONFLICTUPDATE*/structTransitionCaptureState*mt_oc_transition_capture;/*Perplanmapfortupleconversionfromchildtoroot*/TupleConversionMap**mt_per_subplan_tupconv_maps;}ModifyTableState;

核心执行运算符的实现:

/*----------------------------------------------------------------*ExecModifyTable**Performtablemodificationsasrequired,andreturnRETURNINGresults*ifneeded.*----------------------------------------------------------------*/staticTupleTableSlot*ExecModifyTable(PlanState*pstate){ModifyTableState*node=castNode(ModifyTableState,pstate);PartitionTupleRouting*proute=node-gt;mt_partition_tuple_routing;EState*estate=node-gt;ps.state;CmdTypeoperation=node-gt;operation;ResultRelInfo*saved_resultRelInfo;ResultRelInfo*resultRelInfo;PlanState*subplanstate;JunkFilter*junkfilter;TupleTableSlot*slot;TupleTableSlot*planSlot;ItemPointertupleid;ItemPointerDatatuple_ctid;HeapTupleDataoldtupdata;HeapTupleoldtuple;CHECK_FOR_INTERRUPTS();/*ThisshouldNOTgetcalledduringEvalPlanQual;weshouldhavepasseda*subplantreetoEvalPlanQual,instead.Usearuntimetestnotjust*Assertbecausethisconditioniseasytomissintesting.*/if(estate-gt;es_epq_active!=NULL)elog(ERROR,"ModifyTableshouldnotbecalledduringEvalPlanQual");/*Ifwe'vealreadycompletedprocessing,don'ttrytodomore.Weneed*thistestbecauseExecPostprocessPlanmightcallusanextratime,and*oursubplan'snodesaren'tnecessarilyrobustagainstbeingcalled*extratimes.*/if(node-gt;mt_done)returnNULL;/*Onfirstcall,fireBEFORESTATEMENTtriggersbeforeproceeding.*/if(node-gt;fireBSTriggers){fireBSTriggers(node);node-gt;fireBSTriggers=false;}/*Preloadlocalvariables*/resultRelInfo=node-gt;resultRelInfo+node-gt;mt_whichplan;subplanstate=node-gt;mt_plans[node-gt;mt_whichplan];junkfilter=resultRelInfo-gt;ri_junkFilter;/*es_result_relation_infomustpointtothecurrentlyactiveresultrelationwhilewearewithinthisModifyTablenode.*EventhoughModifyTablenodescan'tbenestedstatically,theycanbenested*dynamically(sinceoursubplancouldincludeareferencetoamodifying*CTE).Sowehavetosaveandrestorethecaller'svalue.*/saved_resultRelInfo=estate-gt;es_result_relation_info;estate-gt;es_result_relation_info=resultRelInfo;/*Fetchrowsfromsubplan(s),andexecutetherequiredtablemodificationforeachrow.*/for(;;){/*Resettheper-output-tupleexprcontext.Thisisneededbecause*triggersexpecttousethatcontextasworkspace.It'sabitugly*todothisbelowthetopleveloftheplan,however.Wemightneedtorethinkthislater.*/ResetPerTupleExprContext(estate);/*Resetper-tuplememorycontextusedforprocessingonconflictand*returningclauses,tofreeanyexpressionevaluationstorageallocatedinthepreviouscycle.*/if(pstate-gt;ps_ExprContext)ResetExprContext(pstate-gt;ps_ExprContext);planSlot=ExecProcNode(subplanstate);if(TupIsNull(planSlot)){/*advancetonextsubplanifany*/node-gt;mt_whichplan++;//分区表的update,每个分区分布对应一个subplan,当执行完一个分区再执行下一个分区if(node-gt;mt_whichplanlt;node-gt;mt_nplans){resultRelInfo++;subplanstate=node-gt;mt_plans[node-gt;mt_whichplan];junkfilter=resultRelInfo-gt;ri_junkFilter;estate-gt;es_result_relation_info=resultRelInfo;EvalPlanQualSetPlan(amp;node-gt;mt_epqstate,subplanstate-gt;plan,node-gt;mt_arowmarks[node-gt;mt_whichplan]);/*Preparetoconverttransitiontuplesfromthischild.*/if(node-gt;mt_transition_capture!=NULL){node-gt;mt_transition_capture-gt;tcs_map=tupconv_map_for_subplan(node,node-gt;mt_whichplan);}if(node-gt;mt_oc_transition_capture!=NULL){node-gt;mt_oc_transition_capture-gt;tcs_map=tupconv_map_for_subplan(node,node-gt;mt_whichplan);}continue;}elsebreak;}/*Ensureinputtupleistherightformatforthetargetrelation.*/if(node-gt;mt_scans[node-gt;mt_whichplan]-gt;tts_ops!=planSlot-gt;tts_ops){ExecCopySlot(node-gt;mt_scans[node-gt;mt_whichplan],planSlot);planSlot=node-gt;mt_scans[node-gt;mt_whichplan];}/*IfresultRelInfo-gt;ri_usesFdwDirectModifyistrue,allweneedtodohereiscomputetheRETURNINGexpressions.*/if(resultRelInfo-gt;ri_usesFdwDirectModify){Assert(resultRelInfo-gt;ri_projectReturning);slot=ExecProcessReturning(resultRelInfo-gt;ri_projectReturning,RelationGetRelid(resultRelInfo-gt;ri_RelationDesc),NULL,planSlot);estate-gt;es_result_relation_info=saved_resultRelInfo;returnslot;}EvalPlanQualSetSlot(amp;node-gt;mt_epqstate,planSlot);slot=planSlot;tupleid=NULL;oldtuple=NULL;if(junkfilter!=NULL){/*extractthe'ctid'or'wholerow'junkattribute.*/if(operation==CMD_UPDATE||operation==CMD_DELETE){charrelkind;Datumdatum;boolisNull;relkind=resultRelInfo-gt;ri_RelationDesc-gt;rd_rel-gt;relkind;if(relkind==RELKIND_RELATION||relkind==RELKIND_MATVIEW){datum=ExecGetJunkAttribute(slot,junkfilter-gt;jf_junkAttNo,amp;isNull);/*shouldn'tevergetanullresult...*/if(isNull)elog(ERROR,"ctidisNULL");tupleid=(ItemPointer)DatumGetPointer(datum);tuple_ctid=*tupleid;/*besurewedon'tfreectid!!*/tupleid=amp;tuple_ctid;}/*Usethewholerowattribute,whenavailable,toreconstructtheoldrelationtuple.*/elseif(AttributeNumberIsValid(junkfilter-gt;jf_junkAttNo)){datum=ExecGetJunkAttribute(slot,junkfilter-gt;jf_junkAttNo,amp;isNull);/*shouldn'tevergetanullresult...*/if(isNull)elog(ERROR,"wholerowisNULL");oldtupdata.t_data=DatumGetHeapTupleHeader(datum);oldtupdata.t_len=HeapTupleHeaderGetDatumLength(oldtupdata.t_data);ItemPointerSetInvalid(amp;(oldtupdata.t_self));/*Historically,viewtriggersseeinvalidt_tableOid.*/oldtupdata.t_tableOid=(relkind==RELKIND_VIEW)InvalidOid:RelationGetRelid(resultRelInfo-gt;ri_RelationDesc);oldtuple=amp;oldtupdata;}elseAssert(relkind==RELKIND_FOREIGN_TABLE);}/*applythejunkfilterifneeded.*/if(operation!=CMD_DELETE)slot=ExecFilterJunk(junkfilter,slot);}switch(operation){caseCMD_INSERT:if(proute)/*Preparefortupleroutingifneeded.*/slot=ExecPrepareTupleRouting(node,estate,proute,resultRelInfo,slot);slot=ExecInsert(node,slot,planSlot,NULL,estate-gt;es_result_relation_info,estate,node-gt;canSetTag);if(proute)/*RevertExecPrepareTupleRouting'sstatechange.*/estate-gt;es_result_relation_info=resultRelInfo;break;caseCMD_UPDATE:slot=ExecUpdate(node,tupleid,oldtuple,slot,planSlot,amp;node-gt;mt_epqstate,estate,node-gt;canSetTag);break;caseCMD_DELETE:slot=ExecDelete(node,tupleid,oldtuple,planSlot,amp;node-gt;mt_epqstate,estate,true,node-gt;canSetTag,false/*changingPart*/,NULL,NULL);break;default:elog(ERROR,"unknownoperation");break;}/*IfwegotaRETURNINGresult,returnittocaller.We'llcontinuetheworkonnextcall.*/if(slot){estate-gt;es_result_relation_info=saved_resultRelInfo;returnslot;}}estate-gt;es_result_relation_info=saved_resultRelInfo;/*Restorees_result_relation_infobeforeexiting*/fireASTriggers(node);/*We'redone,butfireAFTERSTATEMENTtriggersbeforeexiting.*/node-gt;mt_done=true;returnNULL;}

我们来看看Update的具体实现。

```c++/*----------------------------------------------------------------*ExecUpdate**note:wecan'trunUPDATEquerieswithtransactionsoffbecauseUPDATEsareactuallyINSERTsandour*scanwillmistakenlyloopforever,updatingthetupleitjustinserted..Thisshouldbefixedbutuntilit*is,wedon'twanttogetstuckinaninfiniteloopwhichcorruptsyourdatabase..**Whenupdatingatable,tupleididentifiesthetupletoupdateandoldtupleisNULL.**ReturnsRETURNINGresultifany,otherwiseNULL.*----------------------------------------------------------------*/staticTupleTableSlot*ExecUpdate(ModifyTableState*mtstate,ItemPointertupleid,HeapTupleoldtuple,TupleTableSlot*slot,TupleTableSlot*planSlot,EPQState*epqstate,EState*estate,boolcanSetTag){ResultRelInfo*resultRelInfo;RelationresultRelationDesc;TM_Resultresult;TM_FailureDatatmfd;List*recheckIndexes=NIL;TupleConversionMap*saved_tcs_map=NULL;/*aborttheoperationifnotrunningtransactions*/if(IsBootstrapProcessingMode())elog(ERROR,"cannotUPDATEduringbootstrap");ExecMaterializeSlot(slot);/*getinformationonthe(current)resultrelation*/resultRelInfo=estate-gt;es_result_relation_info;resultRelationDesc=resultRelInfo-gt;ri_RelationDesc;/*BEFOREROWUPDATETriggers*/if(resultRelInfo-gt;ri_TrigDescamp;amp;resultRelInfo-gt;ri_TrigDesc-gt;trig_update_before_row){if(!ExecBRUpdateTriggers(estate,epqstate,resultRelInfo,tupleid,oldtuple,slot))returnNULL;/*"donothing"*/}/*INSTEADOFROWUPDATETriggers*/if(resultRelInfo-gt;ri_TrigDescamp;amp;resultRelInfo-gt;ri_TrigDesc-gt;trig_update_instead_row){if(!ExecIRUpdateTriggers(estate,resultRelInfo,oldtuple,slot))returnNULL;/*"donothing"*/}elseif(resultRelInfo-gt;ri_FdwRoutine){/*Computestoredgeneratedcolumns*/if(resultRelationDesc-gt;rd_att-gt;constramp;amp;resultRelationDesc-gt;rd_att-gt;constr-gt;has_generated_stored)ExecComputeStoredGenerated(estate,slot,CMD_UPDATE);/*updateinforeigntable:lettheFDWdoit*/slot=resultRelInfo-gt;ri_FdwRoutine-gt;ExecForeignUpdate(estate,resultRelInfo,slot,planSlot);if(slot==NULL)/*"donothing"*/returnNULL;/*AFTERROWTriggersorRETURNINGexpressionsmightreferencethe*tableoidcolumn,so(re-)initializetts_tableOidbeforeevaluatingthem.*/slot-gt;tts_tableOid=RelationGetRelid(resultRelationDesc);}else{LockTupleModelockmode;boolpartition_constraint_failed;boolupdate_indexes;/*Constraintsmightreferencethetableoidcolumn,so(re-)initialize*tts_tableOidbeforeevaluatingthem.*/slot-gt;tts_tableOid=RelationGetRelid(resultRelationDesc);/*Computestoredgeneratedcolumns*/if(resultRelationDesc-gt;rd_att-gt;constramp;amp;resultRelationDesc-gt;rd_att-gt;constr-gt;has_generated_stored)ExecComputeStoredGenerated(estate,slot,CMD_UPDATE);/**CheckanyRLSUPDATEWITHCHECKpolicies**IfwegenerateanewcandidatetupleafterEvalPlanQualtesting,we*mustloopbackhereandrecheckanyRLSpoliciesandconstraints.*(Wedon'tneedtoredotriggers,however.IfthereareanyBEFORE*triggersthentrigger.cwillhavedonetable_tuple_locktolockthe*correcttuple,sothere'snoneedtodothemagain.)*/lreplace:;/*ensureslotisindependent,considere.g.EPQ*/ExecMaterializeSlot(slot);/*Ifpartitionconstraintfails,thisrowmightgetmovedtoanother*partition,inwhichcaseweshouldchecktheRLSCHECKpolicyjust*beforeinsertingintothenewpartition,ratherthandoingithere.*Thisisbecauseatriggeronthatpartitionmightagainchangethe*row.SoskiptheWCOchecksifthepartitionconstraintfails.*/partition_constraint_failed=resultRelInfo-gt;ri_PartitionCheckamp;amp;!ExecPartitionCheck(resultRelInfo,slot,estate,false);if(!partition_constraint_failedamp;amp;resultRelInfo-gt;ri_WithCheckOptions!=NIL){/*ExecWithCheckOptions()willskipanyWCOswhicharenotofthekindwearelookingforatthispoint.*/ExecWithCheckOptions(WCO_RLS_UPDATE_CHECK,resultRelInfo,slot,estate);}/*Ifapartitioncheckfailed,trytomovetherowintotherightpartition.*/if(partition_constraint_failed){booltuple_deleted;TupleTableSlot*ret_slot;TupleTableSlot*orig_slot=slot;TupleTableSlot*epqslot=NULL;PartitionTupleRouting*proute=mtstate-gt;mt_partition_tuple_routing;intmap_index;TupleConversionMap*tupconv_map;/*DisallowanINSERTONCONFLICTDOUPDATEthatcausesthe*originalrowtomigratetoadifferentpartition.Maybethis*canbeimplementedsomeday,butitseemsafringefeaturewith*littleredeemingvalue.*/if(((ModifyTable*)mtstate-gt;ps.plan)-gt;onConflictAction==ONCONFLICT_UPDATE)ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("invalidONUPDATEspecification"),errdetail("Theresulttuplewouldappearinadifferentpartitionthantheoriginaltuple.")));/*WhenanUPDATEisrunonaleafpartition,wewillnothave*partitiontupleroutingsetup.Inthatcase,failwith*partitionconstraintviolationerror.*/if(proute==NULL)ExecPartitionCheckEmitError(resultRelInfo,slot,estate);/*Rowmovement,part1.Deletethetuple,butskipRETURNING*processing.WewanttoreturnrowsfromINSERT.*/ExecDelete(mtstate,tupleid,oldtuple,planSlot,epqstate,estate,false,false/*canSetTag*/,true/*changingPart*/,amp;tuple_deleted,amp;epqslot);/*ForsomereasonifDELETEdidn'thappen(e.g.triggerprevented*it,oritwasalreadydeletedbyself,oritwasconcurrently*deletedbyanothertransaction),thenweshouldskiptheinsert*aswell;otherwise,anUPDATEcouldcauseanincreaseinthe*totalnumberofrowsacrossallpartitions,whichisclearlywrong.**ForanormalUPDATE,thecasewherethetuplehasbeenthe*subjectofaconcurrentUPDATEorDELETEwouldbehandledby*theEvalPlanQualmachinery,butforanUPDATEthatwe've*translatedintoaDELETEfromthispartitionandanINSERTinto*someotherpartition,that'snotavailable,becauseCTIDchains*can'tspan relationboundaries.Wemimicthesemanticstoa*limitedextentbyskippingtheINSERTiftheDELETEfailsto*findatuple.Thisensuresthattwoconcurrentattemptsto*UPDATEthesametupleatthesametimecan'tturnonetuple*intotwo,andthatanUPDATEofajust-deletedtuplecan'tresurrectit.*/if(!tuple_deleted){/**epqslotwillbetypicallyNULL.ButwhenExecDelete()*findsthatanothertransactionhasconcurrentlyupdatedthe*samerow,itre-fetchestherow,skipsthedelete,and*epqslotissettothere-fetchedtupleslot.Inthatcase,*weneedtodoallthechecksagain.*/if(TupIsNull(epqslot))returnNULL;else{slot=ExecFilterJunk(resultRelInfo-gt;ri_junkFilter,epqslot);gotolreplace;}}/*Updatessetthetransitioncapturemaponlywhenanewsubplan*ischosen.Butforinserts,itissetforeachrow.Soafter*INSERT,weneedtorevertbacktothemapcreatedforUPDATE;*otherwisethenextUPDATEwillincorrectlyusetheonecreated*forINSERT.SofirstsavetheonecreatedforUPDATE.*/if(mtstate-gt;mt_transition_capture)saved_tcs_map=mtstate-gt;mt_transition_capture-gt;tcs_map;/*resultRelInfoisoneoftheper-subplanresultRelInfos.Sowe*shouldconvertthetupleintoroot'stupledescriptor,since*ExecInsert()startsthesearchfromroot.Thetupleconversion*maplistisintheorderofmtstate-gt;resultRelInfo[],soto*retrievetheoneforthisresultRel,weneedtoknowthe*positionoftheresultRelinmtstate-gt;resultRelInfo[].*/map_index=resultRelInfo-mtstate-gt;resultRelInfo;Assert(map_indexgt;=0amp;amp;map_indexlt;mtstate-gt;mt_nplans);tupconv_map=tupconv_map_for_subplan(mtstate,map_index);if(tupconv_map!=NULL)slot=execute_attr_map_slot(tupconv_map-gt;attrMap,slot,mtstate-gt;mt_root_tuple_slot);/*Preparefortuplerouting,makingitlooklikewe'reinsertingintotheroot.*/Assert(mtstate-gt;rootResultRelInfo!=NULL);slot=ExecPrepareTupleRouting(mtstate,estate,proute,mtstate-gt;rootResultRelInfo,slot);ret_slot=ExecInsert(mtstate,slot,planSlot,orig_slot,resultRelInfo,estate,canSetTag);/*RevertExecPrepareTupleRouting'snodechange.*/estate-gt;es_result_relation_info=resultRelInfo;if(mtstate-gt;mt_transition_capture){mtstate-gt;mt_transition_capture-gt;tcs_original_insert_tuple=NULL;mtstate-gt;mt_transition_capture-gt;tcs_map=saved_tcs_map;}returnret_slot;}/*Checktheconstraintsofthetuple.We'vealreadycheckedthe*partitionconstraintabove;however,wemuststillensurethetuple*passesallotherconstraints,sowewillcallExecConstraints()and*haveitvalidateallremainingchecks.*/if(resultRelationDesc-gt;rd_att-gt;constr)ExecConstraints(resultRelInfo,slot,estate);/*replacetheheaptuple**Note:ifes_crosscheck_snapshotisn'tInvalidSnapshot,wecheck*thattherowtobeupdatedisvisibletothatsnapshot,andthrowa*can't-serializeerrorifnot.Thisisaspecial-casebehavior*neededforreferentialintegrityupdatesintransaction-snapshotmodetransactions.*/result=table_tuple_update(resultRelationDesc,tupleid,slot,estate-gt;es_output_cid,estate-gt;es_snapshot,estate-gt;es_crosscheck_snapshot,true/*waitforcommit*/,amp;tmfd,amp;lockmode,amp;update_indexes);switch(result){caseTM_SelfModified:/*Thetargettuplewasalreadyupdatedordeletedbythe*currentcommand,orbyalatercommandinthecurrent*transaction.TheformercaseispossibleinajoinUPDATE*wheremultipletuplesjointothesametargettuple.This*isprettyquestionable,butPostgreshasalwaysallowedit:*wejustexecutethefirstupdateactionandignore*additionalupdateattempts.**Thelattercasearisesifthetupleismodifiedbya*commandinaBEFOREtrigger,orperhapsbyacommandina*volatilefunctionusedinthequery.Insuchsituationswe*shouldnotignoretheupdate,butitisequallyunsafeto*proceed.Wedon'twanttodiscardtheoriginalUPDATE*whilekeepingthetriggeredactionsbasedonit;andwe*havenoprincipledwaytomergethisupdatewiththe*previousones.Sothrowinganerroristheonlysafe*course.**Ifatriggeractuallyintendsthistypeofinteraction,it*canre-executetheUPDATE(assumingitcanfigureouthow)*andthenreturnNULLtocanceltheouterupdate.*/if(tmfd.cmax!=estate-gt;es_output_cid)ereport(ERROR,(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),errmsg("tupletobeupdatedwasalreadymodifiedbyanoperationtriggeredbythecurrentcommand"),errhint("ConsiderusinganAFTERtriggerinsteadofaBEFOREtriggertopropagatechangestootherrows.")));/*Else,alreadyupdatedbyself;nothingtodo*/returnNULL;caseTM_Ok:break;caseTM_Updated:{TupleTableSlot*inputslot;TupleTableSlot*epqslot;if(IsolationUsesXactSnapshot())ereport(ERROR,(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),errmsg("couldnotserializeaccessduetoconcurrentupdate")));/*Alreadyknowthatwe'regoingtoneedtodoEPQ,sofetchtupledirectlyintotherightslot.*/inputslot=EvalPlanQualSlot(epqstate,resultRelationDesc,resultRelInfo-gt;ri_RangeTableIndex);result=table_tuple_lock(resultRelationDesc,tupleid,estate-gt;es_snapshot,inputslot,estate-gt;es_output_cid,lockmode,LockWaitBlock,TUPLE_LOCK_FLAG_FIND_LAST_VERSION,amp;tmfd);switch(result){caseTM_Ok:Assert(tmfd.traversed);epqslot=EvalPlanQual(epqstate,resultRelationDesc,resultRelInfo-gt;ri_RangeTableIndex,inputslot);if(TupIsNull(epqslot))/*Tuplenotpassingqualsanymore,exiting...*/returnNULL;slot=ExecFilterJunk(resultRelInfo-gt;ri_junkFilter,epqslot);gotolreplace;caseTM_Deleted:/*tuplealreadydeleted;nothingtodo*/returnNULL;caseTM_SelfModified:/**Thiscanbereachedwhenfollowinganupdatechainfromatupleupdatedbyanothersession,*reachingatuplethatwasalreadyupdatedinthistransaction.Ifpreviouslymodifiedby*thiscommand,ignoretheredundantupdate,otherwiseerrorout.**SeealsoTM_SelfModifiedresponsetotable_tuple_update()above.*/if(tmfd.cmax!=estate-gt;es_output_cid)ereport(ERROR,(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),errmsg("tupletobeupdatedwasalreadymodifiedbyanoperationtriggeredbythecurrentcommand"),errhint("ConsiderusinganAFTERtriggerinsteadofaBEFOREtriggertopropagatechangestootherrows.")));returnNULL;default:/*seetable_tuple_lockcallinExecDelete()*/elog(ERROR,"unexpectedtable_tuple_lockstatus:%u",result);returnNULL;}}break;caseTM_Deleted:if(IsolationUsesXactSnapshot())ereport(ERROR,(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),errmsg("couldnotserializeaccessduetoconcurrentdelete")));/*tuplealreadydeleted;nothingtodo*/returnNULL;default:elog(ERROR,"unrecognizedtable_tuple_updatestatus:%u",result);returnNULL;}/*insertindexentriesfortupleifnecessary*/if(resultRelInfo-gt;ri_NumIndicesgt;0amp;amp;update_indexes)recheckIndexes=ExecInsertIndexTuples(slot,estate,false,NULL,NIL);}if(canSetTag)(estate-gt;es_processed)++;/*AFTERROWUPDATETriggers*/ExecARUpdateTriggers(estate,resultRelInfo,tupleid,oldtuple,slot,recheckIndexes,mtstate-gt;operation==CMD_INSERTmtstate-gt;mt_oc_transition_capture:mtstate-gt;mt_transition_capture);list_free(recheckIndexes);/*CheckanyWITHCHECKOPTIONconstraintsfromparentviews.Weare*requiredtodothisaftertestingallconstraintsanduniqueness*violationspertheSQLspec,sowedoitafteractuallyupdatingthe*recordintheheapandallindexes.**ExecWithCheckOptions()willskipanyWCOswhicharenotofthekindwe*arelookingforatthispoint.*/if(resultRelInfo-gt;ri_WithCheckOptions!=NIL)ExecWithCheckOptions(WCO_VIEW_CHECK,resultRelInfo,slot,estate);if(resultRelInfo-gt;ri_projectReturning)/*ProcessRETURNINGifpresent*/returnExecProcessReturning(resultRelInfo-gt;ri_projectReturning,RelationGetRelid(resultRelationDesc),slot,planSlot);returnNULL;}

接下来,涉及到存储引擎。让我们关注它的外部接口输入参数。关注这四个参数:

要修改的关系表(调用者必须持有suitablelock)(要更新的表)

要替换的旧元组的Otid-Tid(要更新的元组ID对应于旧元组。更新后相当于插入了一个新的元组,旧元组的tid值要更新为新的tid值)

slot-要存储的新构造的元组数据(新元组的值)

Cid-update命令id(用于可见性测试,如果成功则存储到Cmax/cmin中)(Cid值,与事务相关)执行程序级别的更新操作符基于存储引擎提供的底层table_tuple_update接口。是我们编写ExecUpdate和ExecModifyTable的基础。

/**Updateatuple.*Inputparameters:*relation-tabletobemodified(callermustholdsuitablelock)*otid-TIDofoldtupletobereplaced*slot-newlyconstructedtupledatatostore*cid-updatecommandID(usedforvisibilitytest,andstoredintocmax/cminifsuccessful)*crosscheck-ifnotInvalidSnapshot,alsocheckoldtupleagainstthis*wait-trueifshouldwaitforanyconflictingupdatetocommit/abort*Outputparameters:*tmfd-filledinfailurecases(seebelow)*lockmode-filledwithlockmodeacquiredontuple*update_indexes-insuccesscasesthisissettotrueifnewindexentriesarerequiredforthistuple**Normal,successfulreturnvalueisTM_Ok,whichmeanswedidactuallyupdateit.*/staticinlineTM_Resulttable_tuple_update(Relationrel,ItemPointerotid,TupleTableSlot*slot,CommandIdcid,Snapshotsnapshot,Snapshotcrosscheck,boolwait,TM_FailureData*tmfd,LockTupleMode*lockmode,bool*update_indexes){returnrel-gt;rd_tableam-gt;tuple_update(rel,otid,slot,cid,snapshot,crosscheck,wait,tmfd,lockmode,update_indexes);}


事务

这部分主要是理解PG中的update语句并不是原地更新元组,而是插入一个新的元组。因为PG实现MVCC和Mysql的方式不同,Oracle并不是通过撤销日志来实现的,相当于把撤销日志记录在原始表中,而不是存储在一个单独的地方。我就不细说了。内容太多了。交易部分我以后再分析。

好的,内容很多。在分析源代码的时候,涉及到大量的知识点和逻辑。我们最好每次分析只专注一个主干,否则全部分析,最后会很乱。让我们在这里完成分析。


总结

本文关于Postgres中UPDATE语句的源代码分析到此为止。关于Postgres中更新源代码的更多信息

0

精彩评论

暂无评论...
验证码 换一张
取 消