PostgreSQL中Review exec_simple_query函数的实现逻辑是什么
这篇文章主要介绍“PostgreSQL中Review exec_simple_query函数的实现逻辑是什么”,在日常操作中,相信很多人在PostgreSQL中Review exec_simple_query函数的实现逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中Review exec_simple_query函数的实现逻辑是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
源码解读exec_simple_query函数由PostgresMain函数调用,其调用栈如下:
#30x00000000008c5c35inexec_simple_query(query_string=0x2eed1a8"select*fromt1;")atpostgres.c:1050#40x00000000008ca0f8inPostgresMain(argc=1,argv=0x2f16cb8,dbname=0x2f16b20"testdb",username=0x2ee9d48"xdb")atpostgres.c:4159#50x0000000000825880inBackendRun(port=0x2f0eb00)atpostmaster.c:4361#60x0000000000824fe4inBackendStartup(port=0x2f0eb00)atpostmaster.c:4033#70x0000000000821371inServerLoop()atpostmaster.c:1706#80x0000000000820c09inPostmasterMain(argc=1,argv=0x2ee7d00)atpostmaster.c:1379#90x0000000000747ea7inmain(argc=1,argv=0x2ee7d00)atmain.c:228
该函数的实现逻辑详见代码注释.
/**exec_simple_query**Executea"simpleQuery"protocolmessage.*响应并执行"simpleQuery"协议消息请求*//*输入:query_string-SQL语句输出:无*/staticvoidexec_simple_query(constchar*query_string){CommandDestdest=whereToSendOutput;//输出到哪里的定义MemoryContextoldcontext;//存储原内存上下文List*parsetree_list;//分析树列表ListCell*parsetree_item;//分析树中的ITEMboolsave_log_statement_stats=log_statement_stats;//是否保存统计信息,falseboolwas_logged=false;//Log?booluse_implicit_block;//是否使用隐式事务块charmsec_str[32];/**Reportquerytovariousmonitoringfacilities.*监控信息*/debug_query_string=query_string;pgstat_report_activity(STATE_RUNNING,query_string);//统计信息TRACE_POSTGRESQL_QUERY_START(query_string);/**Weusesave_log_statement_statssoShowUsagedoesn'treportincorrect*resultsbecauseResetUsagewasn'tcalled.*如save_log_statement_stats为T,则调用ResetUsage函数*/if(save_log_statement_stats)ResetUsage();/**Startupatransactioncommand.Allqueriesgeneratedbythe*query_stringwillbeinthissamecommandblock,*unless*wefinda*BEGIN/COMMIT/ABORTstatement;wehavetoforceanewxactcommandafter*oneofthose,elsebadthingswillhappeninxact.c.(Notethatthis*willnormallychangecurrentmemorycontext.)*启动一个事务命令。*由query_string生成的所有查询都将位于同一个命令块中,*除非*找到BEGIN/COMMIT/ABORT语句;*必须在其中一个命令之后强制执行新的xact命令,否则xact.c中会发生意想不到事情。*(注意,这通常会改变当前内存上下文。)*/start_xact_command();//启动事务/**Zapanypre-existingunnamedstatement.(Whilenotstrictlynecessary,*itseemsbesttodefinesimple-Querymodeasifitusedtheunnamed*statementandportal;thisensureswerecoveranystorageusedbyprior*unnamedoperations.)*删除任何预先存在的未命名语句。*(虽然不是严格必要的,但最好定义简单查询模式,就像使用未命名语句和门户接口一样;*这确保我们恢复以前未命名操作所使用的存储信息。)*/drop_unnamed_stmt();//清除未命名语句/**Switchtoappropriatecontextforconstructingparsetrees.*切换至合适的内存上下文*/oldcontext=MemoryContextSwitchTo(MessageContext);//切换内存上下文/**Dobasicparsingofthequeryorqueries(thisshouldbesafeevenif*weareinabortedtransactionstate!)*执行查询(或多个查询)的基本解析*(即使我们处于中止的事务状态,这也应该是安全的!)*/parsetree_list=pg_parse_query(query_string);//解析输入的查询语句,获得解析树List(元素是RawStmtnodes)/*Logimmediatelyifdictatedbylog_statement*///如需要记录日志,则Log这些信息if(check_log_statement(parsetree_list))//日志记录{ereport(LOG,(errmsg("statement:%s",query_string),errhidestmt(true),errdetail_execute(parsetree_list)));was_logged=true;}/**Switchbacktotransactioncontexttoentertheloop.*切换回去原事务上下文*/MemoryContextSwitchTo(oldcontext);//切换回原内存上下文/**Forhistoricalreasons,ifmultipleSQLstatementsaregivenina*single"simpleQuery"message,weexecutethemasasingletransaction,*unlessexplicittransactioncontrolcommandsareincludedtomake*portionsofthelistbeseparatetransactions.Torepresentthis*behaviorproperlyinthetransactionmachinery,weusean"implicit"*transactionblock.*由于历史原因,如果在单个“简单查询”消息中给出了多个SQL语句,那么我们将作为单个事务执行它们,*除非包含显式事务控制命令,使链表中的部分成为单独的事务。*为了在事务机制中正确地表示这种行为,使用了一个“隐式”事务块。*///如果分析树条目>1,使用隐式事务块(多条SQL语句在同一个事务中)use_implicit_block=(list_length(parsetree_list)>1);/**Runthroughtherawparsetree(s)andprocesseachone.*对分析树中的每一个条目进行处理*/foreach(parsetree_item,parsetree_list)//{RawStmt*parsetree=lfirst_node(RawStmt,parsetree_item);//分析树List中的元素为RawStmt指针类型boolsnapshot_set=false;//是否设置快照?constchar*commandTag;//命令标识charcompletionTag[COMPLETION_TAG_BUFSIZE];//完成标记,如INSERT01之类的字符串List*querytree_list,//查询树List*plantree_list;//执行计划ListPortalportal;//“门户”变量DestReceiver*receiver;//目标接收端int16format;///**Getthecommandnameforuseinstatusdisplay(italsobecomesthe*defaultcompletiontag,downinsidePortalRun).Setps_statusand*doanyspecialstart-of-SQL-commandprocessingneededbythe*destination.*获取用于状态显示的命令名称(在PortalRun内部,它也成为默认的完成标记)。*设置ps_status并执行目标语句所需要的start-of-SQL-command处理。*/commandTag=CreateCommandTag(parsetree->stmt);//创建命令标记,插入数据则为INSERTset_ps_display(commandTag,false);BeginCommand(commandTag,dest);//doNothing!/**Ifweareinanabortedtransaction,rejectallcommandsexcept*COMMIT/ABORT.Itisimportantthatthistestoccurbeforewetry*todoparseanalysis,rewrite,orplanning,sinceallthosephases*trytododatabaseaccesses,whichmayfailinabortstate.(It*mightbesafetoallowsomeadditionalutilitycommandsinthis*state,butnotmany...)*如果我们处于事务中止状态中,拒绝除COMMIT/ABORT之外的所有命令。*在尝试进行解析分析、重写或计划之前进行此测试是很重要的,*因为所有这些阶段都尝试进行数据库访问,而在abort状态下可能会失败。*(在这种状态下允许一些额外的实用程序命令可能是安全的,但不是很多……)*/if(IsAbortedTransactionBlockState()&&!IsTransactionExitStmt(parsetree->stmt))ereport(ERROR,(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),errmsg("currenttransactionisaborted,""commandsignoreduntilendoftransactionblock"),errdetail_abort()));/*Makesureweareinatransactioncommand*/start_xact_command();//确认在事务中/**Ifusinganimplicittransactionblock,andwe'renotalreadyina*transactionblock,startanimplicitblocktoforcethisstatement*tobegroupedtogetherwithanyfollowingones.(Wemustdothis*eachtimethroughtheloop;otherwise,aCOMMIT/ROLLBACKinthe*listwouldcauselaterstatementstonotbegrouped.)*如果使用隐式事务块(还没有使用atransaction块),启动一个隐式事务块来强制将该语句与以下语句组合在一起。*(我们每次通过循环时都必须这样做;否则,链表中的提交/回滚将导致后面的语句没有分组。*/if(use_implicit_block)BeginImplicitTransactionBlock();//隐式事务,进入事务块/*Ifwegotacancelsignalinparsingorpriorcommand,quit*///如果在解析或者解析之前接收到取消的信号,则退出CHECK_FOR_INTERRUPTS();/**Setupasnapshotifparseanalysis/planningwillneedone.*如果解析/分析/计划过程需要snapshot,则创建一个*/if(analyze_requires_snapshot(parsetree))//是否需要快照进行分析?增删改查均需要{PushActiveSnapshot(GetTransactionSnapshot());//活动快照入栈snapshot_set=true;}/**OKtoanalyze,rewrite,andplanthisquery.*准备工作妥当,可以进行分析/重写和计划查询语句了**Switchtoappropriatecontextforconstructingquerytrees(again,*thesemustoutlivetheexecutioncontext).*切换至合适的内存上下文中*/oldcontext=MemoryContextSwitchTo(MessageContext);//切换内存上下文//根据分析树获得查询树,返回List(元素为Query)querytree_list=pg_analyze_and_rewrite(parsetree,query_string,NULL,0,NULL);//根据查询树获取计划树,返回List(元素为PlannedStmt)plantree_list=pg_plan_queries(querytree_list,CURSOR_OPT_PARALLEL_OK,NULL);/*Donewiththesnapshotusedforparsing/planning*///启用了快照,则出栈if(snapshot_set)PopActiveSnapshot();///*Ifwegotacancelsignalinanalysisorplanning,quit*///如接收到取消信号,则退出CHECK_FOR_INTERRUPTS();/**Createunnamedportaltorunthequeryorqueriesin.Ifthere*alreadyisone,silentlydropit.*创建匿名的门户接口来执行查询,如Portal已存在,则先dropit*/portal=CreatePortal("",true,true);//创建匿名Portal变量/*Don'tdisplaytheportalinpg_cursors*///该portal不在pg_cursors中出现portal->visible=false;/**Wedon'thavetocopyanythingintotheportal,becauseeverything*wearepassinghereisinMessageContext,whichwilloutlivethe*portalanyway.*不需要将任何内容复制到Portal中,*因为在这里传递的所有内容都在MessageContext中,它的生命周期将比Portal更长。*/PortalDefineQuery(portal,NULL,query_string,commandTag,plantree_list,NULL);//给Portal变量赋值/**Starttheportal.Noparametershere.*启动Portal,不需要参数*/PortalStart(portal,NULL,0,InvalidSnapshot);//为PortalRun作准备/**Selecttheappropriateoutputformat:textunlesswearedoinga*FETCHfromabinarycursor.(Prettygrottytohavetodothishere*---butitavoidsgrottinessinotherplaces.Ah,thejoysof*backwardcompatibility...)*选择适当的输出格式:文本,除了我们正在从二进制游标进行提取。*(不得不在这里这样做实在是太糟糕了——但在其他地方却避免了这种情况。啊,向后兼容的乐趣……*/format=0;/*默认为TEXT;TEXTisdefault*/if(IsA(parsetree->stmt,FetchStmt)){FetchStmt*stmt=(FetchStmt*)parsetree->stmt;if(!stmt->ismove){Portalfportal=GetPortalByName(stmt->portalname);if(PortalIsValid(fportal)&&(fportal->cursorOptions&CURSOR_OPT_BINARY))format=1;/*二进制格式;BINARY*/}}PortalSetResultFormat(portal,1,&format);//设置结果返回的格式,默认为TEXT/**Nowwecancreatethedestinationreceiverobject.*现在可以创建目标接收器对象了*///创建目标接收器(如使用psql则为:printtupDestReceiver)receiver=CreateDestReceiver(dest);if(dest==DestRemote)SetRemoteDestReceiverParams(receiver,portal);/**Switchbacktotransactioncontextforexecution.*切换回原内存上下文*/MemoryContextSwitchTo(oldcontext);///**Runtheportaltocompletion,andthendropit(andthereceiver).*执行PortalRun,然后清除*/(void)PortalRun(portal,FETCH_ALL,true,/*alwaystoplevel*/true,receiver,receiver,completionTag);//执行//执行完毕,销毁接收器receiver->rDestroy(receiver);//清除Portal中的资源PortalDrop(portal,false);if(lnext(parsetree_item)==NULL)//所有语句已执行完毕{/**Ifthisisthelastparsetreeofthequerystring,closedown*transactionstatementbeforereportingcommand-complete.This*issothatanyend-of-transactionerrorsarereportedbefore*thecommand-completemessageisissued,toavoidconfusing*clientswhowillexpecteitheracommand-completemessageoran*error,notoneandthentheother.Also,ifwe'reusingan*implicittransactionblock,wemustclosethatoutfirst.*如果这是查询字符串的最后一个parsetree,在报告命令完成之前关闭事务语句。*这样,在发出命令完整的消息之前就会报告任何事务结束错误,以避免混淆视听,*因为客户端希望看到命令完整的消息或错误,而不是一个错误,然后是另一个错误。*另外,如果我们使用隐式事务块,我们必须首先关闭它。*/if(use_implicit_block)EndImplicitTransactionBlock();//结束事务finish_xact_command();//结束事务}elseif(IsA(parsetree->stmt,TransactionStmt))//事务语句?BEGIN/COMMIT/ABORT...{/**Ifthiswasatransactioncontrolstatement,commitit.Wewill*startanewxactcommandforthenextcommand.*如果这是一个事务控制语句,提交此语句。*我们将为下一个命令启动一个新的xact命令。*/finish_xact_command();}else{/**WeneedaCommandCounterIncrementaftereveryquery,except*thosethatstartorendatransactionblock.*在每次查询之后,我们都需要一个CommandCounterIncrement,除了那些启动或结束事务块的查询。*/CommandCounterIncrement();//命令+1(对应Tuple中的cid)}/**Tellclientthatwe'redonewiththisquery.Noteweemitexactly*oneEndCommandreportforeachrawparsetree,thusoneforeachSQL*commandtheclientsent,regardlessofrewriting.(Butacommand*abortedbyerrorwillnotsendanEndCommandreportatall.)*告诉客户端已经完成了这个查询。注意,对于每个原始的parsetree,只发出一个EndCommand报告,*因此,对于客户机发送的每个SQL命令,只发出一个EndCommand报告,而不考虑重写。*(注:由于错误而中止的命令根本不会发送EndCommand报告。)*/EndCommand(completionTag,dest);//命令Done}/*endloopoverparsetrees*///所有语句结束/**Closedowntransactionstatement,ifoneisopen.(Thiswillonlydo*somethingiftheparsetreelistwasempty;otherwisethelastloop*iterationalreadydidit.)*如果事务是打开的,则关闭。*(只有当parsetree链表为空时,才会执行某些操作;否则,最后一次循环迭代已经完成了。*/finish_xact_command();/**Iftherewerenoparsetrees,returnEmptyQueryResponsemessage.*如果parsetrees链表已清空,返回EmptyQueryResponse消息*/if(!parsetree_list)NullCommand(dest);/**Emitdurationloggingifappropriate.*如需要记录日志,则在合适的时候记录*/switch(check_log_duration(msec_str,was_logged)){case1:ereport(LOG,(errmsg("duration:%sms",msec_str),errhidestmt(true)));break;case2:ereport(LOG,(errmsg("duration:%smsstatement:%s",msec_str,query_string),errhidestmt(true),errdetail_execute(parsetree_list)));break;}if(save_log_statement_stats)ShowUsage("QUERYSTATISTICS");TRACE_POSTGRESQL_QUERY_DONE(query_string);debug_query_string=NULL;}
到此,关于“PostgreSQL中Review exec_simple_query函数的实现逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。