PostgreSQL中Review exec_simple_query函数的实现逻辑是什么
发表于:2024-11-19 作者:千家信息网编辑
千家信息网最后更新 2024年11月19日,这篇文章主要介绍"PostgreSQL中Review exec_simple_query函数的实现逻辑是什么",在日常操作中,相信很多人在PostgreSQL中Review exec_simple_q
千家信息网最后更新 2024年11月19日PostgreSQL中Review exec_simple_query函数的实现逻辑是什么
这篇文章主要介绍"PostgreSQL中Review exec_simple_query函数的实现逻辑是什么",在日常操作中,相信很多人在PostgreSQL中Review exec_simple_query函数的实现逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"PostgreSQL中Review exec_simple_query函数的实现逻辑是什么"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
源码解读
exec_simple_query函数由PostgresMain函数调用,其调用栈如下:
#3 0x00000000008c5c35 in exec_simple_query (query_string=0x2eed1a8 "select * from t1;") at postgres.c:1050#4 0x00000000008ca0f8 in PostgresMain (argc=1, argv=0x2f16cb8, dbname=0x2f16b20 "testdb", username=0x2ee9d48 "xdb") at postgres.c:4159#5 0x0000000000825880 in BackendRun (port=0x2f0eb00) at postmaster.c:4361#6 0x0000000000824fe4 in BackendStartup (port=0x2f0eb00) at postmaster.c:4033#7 0x0000000000821371 in ServerLoop () at postmaster.c:1706#8 0x0000000000820c09 in PostmasterMain (argc=1, argv=0x2ee7d00) at postmaster.c:1379#9 0x0000000000747ea7 in main (argc=1, argv=0x2ee7d00) at main.c:228
该函数的实现逻辑详见代码注释.
/* * exec_simple_query * * Execute a "simple Query" protocol message. * 响应并执行"simple Query"协议消息请求 *//*输入: query_string-SQL语句输出: 无*/static voidexec_simple_query(const char *query_string){ CommandDest dest = whereToSendOutput;//输出到哪里的定义 MemoryContext oldcontext;//存储原内存上下文 List *parsetree_list;//分析树列表 ListCell *parsetree_item;//分析树中的ITEM bool save_log_statement_stats = log_statement_stats;//是否保存统计信息,false bool was_logged = false;//Log? bool use_implicit_block;//是否使用隐式事务块 char msec_str[32]; /* * Report query to various monitoring facilities. * 监控信息 */ debug_query_string = query_string; pgstat_report_activity(STATE_RUNNING, query_string);//统计信息 TRACE_POSTGRESQL_QUERY_START(query_string); /* * We use save_log_statement_stats so ShowUsage doesn't report incorrect * results because ResetUsage wasn't called. * 如save_log_statement_stats为T,则调用ResetUsage函数 */ if (save_log_statement_stats) ResetUsage(); /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after * one of those, else bad things will happen in xact.c. (Note that this * will normally change current memory context.) * 启动一个事务命令。 * 由query_string生成的所有查询都将位于同一个命令块中,*除非*找到BEGIN/COMMIT/ABORT语句; * 必须在其中一个命令之后强制执行新的xact命令,否则xact.c中会发生意想不到事情。 * (注意,这通常会改变当前内存上下文。) */ start_xact_command();//启动事务 /* * Zap any pre-existing unnamed statement. (While not strictly necessary, * it seems best to define simple-Query mode as if it used the unnamed * statement and portal; this ensures we recover any storage used by prior * unnamed operations.) * 删除任何预先存在的未命名语句。 * (虽然不是严格必要的,但最好定义简单查询模式,就像使用未命名语句和门户接口一样; * 这确保我们恢复以前未命名操作所使用的存储信息。) */ drop_unnamed_stmt();//清除未命名语句 /* * Switch to appropriate context for constructing parsetrees. * 切换至合适的内存上下文 */ oldcontext = MemoryContextSwitchTo(MessageContext);//切换内存上下文 /* * Do basic parsing of the query or queries (this should be safe even if * we are in aborted transaction state!) * 执行查询(或多个查询)的基本解析 * (即使我们处于中止的事务状态,这也应该是安全的!) */ parsetree_list = pg_parse_query(query_string);//解析输入的查询语句,获得解析树List(元素是RawStmt nodes) /* Log immediately if dictated by log_statement */ //如需要记录日志,则Log这些信息 if (check_log_statement(parsetree_list))//日志记录 { ereport(LOG, (errmsg("statement: %s", query_string), errhidestmt(true), errdetail_execute(parsetree_list))); was_logged = true; } /* * Switch back to transaction context to enter the loop. * 切换回去原事务上下文 */ MemoryContextSwitchTo(oldcontext);//切换回原内存上下文 /* * For historical reasons, if multiple SQL statements are given in a * single "simple Query" message, we execute them as a single transaction, * unless explicit transaction control commands are included to make * portions of the list be separate transactions. To represent this * behavior properly in the transaction machinery, we use an "implicit" * transaction block. * 由于历史原因,如果在单个"简单查询"消息中给出了多个SQL语句,那么我们将作为单个事务执行它们, * 除非包含显式事务控制命令,使链表中的部分成为单独的事务。 * 为了在事务机制中正确地表示这种行为,使用了一个"隐式"事务块。 */ //如果分析树条目>1,使用隐式事务块(多条SQL语句在同一个事务中) use_implicit_block = (list_length(parsetree_list) > 1); /* * Run through the raw parsetree(s) and process each one. * 对分析树中的每一个条目进行处理 */ foreach(parsetree_item, parsetree_list)// { RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);//分析树List中的元素为RawStmt指针类型 bool snapshot_set = false;//是否设置快照? const char *commandTag;//命令标识 char completionTag[COMPLETION_TAG_BUFSIZE];//完成标记,如INSERT 0 1之类的字符串 List *querytree_list,//查询树List *plantree_list;//执行计划List Portal portal;//"门户"变量 DestReceiver *receiver;//目标接收端 int16 format;// /* * Get the command name for use in status display (it also becomes the * default completion tag, down inside PortalRun). Set ps_status and * do any special start-of-SQL-command processing needed by the * destination. * 获取用于状态显示的命令名称(在PortalRun内部,它也成为默认的完成标记)。 * 设置ps_status并执行目标语句所需要的start-of-SQL-command处理。 */ commandTag = CreateCommandTag(parsetree->stmt);//创建命令标记,插入数据则为INSERT set_ps_display(commandTag, false); BeginCommand(commandTag, dest);//do Nothing! /* * If we are in an aborted transaction, reject all commands except * COMMIT/ABORT. It is important that this test occur before we try * to do parse analysis, rewrite, or planning, since all those phases * try to do database accesses, which may fail in abort state. (It * might be safe to allow some additional utility commands in this * state, but not many...) * 如果我们处于事务中止状态中,拒绝除COMMIT/ABORT之外的所有命令。 * 在尝试进行解析分析、重写或计划之前进行此测试是很重要的, * 因为所有这些阶段都尝试进行数据库访问,而在abort状态下可能会失败。 * (在这种状态下允许一些额外的实用程序命令可能是安全的,但不是很多……) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(parsetree->stmt)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block"), errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command();//确认在事务中 /* * If using an implicit transaction block, and we're not already in a * transaction block, start an implicit block to force this statement * to be grouped together with any following ones. (We must do this * each time through the loop; otherwise, a COMMIT/ROLLBACK in the * list would cause later statements to not be grouped.) * 如果使用隐式事务块(还没有使用atransaction块),启动一个隐式事务块来强制将该语句与以下语句组合在一起。 * (我们每次通过循环时都必须这样做;否则,链表中的提交/回滚将导致后面的语句没有分组。 */ if (use_implicit_block) BeginImplicitTransactionBlock();//隐式事务,进入事务块 /* If we got a cancel signal in parsing or prior command, quit */ //如果在解析或者解析之前接收到取消的信号,则退出 CHECK_FOR_INTERRUPTS(); /* * Set up a snapshot if parse analysis/planning will need one. * 如果解析/分析/计划过程需要snapshot,则创建一个 */ if (analyze_requires_snapshot(parsetree))//是否需要快照进行分析?增删改查均需要 { PushActiveSnapshot(GetTransactionSnapshot());//活动快照入栈 snapshot_set = true; } /* * OK to analyze, rewrite, and plan this query. * 准备工作妥当,可以进行分析/重写和计划查询语句了 * * Switch to appropriate context for constructing querytrees (again, * these must outlive the execution context). * 切换至合适的内存上下文中 */ 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); /* Done with the snapshot used for parsing/planning */ //启用了快照,则出栈 if (snapshot_set) PopActiveSnapshot();// /* If we got a cancel signal in analysis or planning, quit */ //如接收到取消信号,则退出 CHECK_FOR_INTERRUPTS(); /* * Create unnamed portal to run the query or queries in. If there * already is one, silently drop it. * 创建匿名的门户接口来执行查询,如Portal已存在,则先drop it */ portal = CreatePortal("", true, true);//创建匿名Portal变量 /* Don't display the portal in pg_cursors */ //该portal不在pg_cursors中出现 portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in MessageContext, which will outlive the * portal anyway. * 不需要将任何内容复制到Portal中, * 因为在这里传递的所有内容都在MessageContext中,它的生命周期将比Portal更长。 */ PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL);//给Portal变量赋值 /* * Start the portal. No parameters here. * 启动Portal,不需要参数 */ PortalStart(portal, NULL, 0, InvalidSnapshot);//为PortalRun作准备 /* * Select the appropriate output format: text unless we are doing a * FETCH from a binary cursor. (Pretty grotty to have to do this here * --- but it avoids grottiness in other places. Ah, the joys of * backward compatibility...) * 选择适当的输出格式:文本,除了我们正在从二进制游标进行提取。 * (不得不在这里这样做实在是太糟糕了--但在其他地方却避免了这种情况。啊,向后兼容的乐趣…… */ format = 0; /* 默认为TEXT;TEXT is default */ if (IsA(parsetree->stmt, FetchStmt)) { FetchStmt *stmt = (FetchStmt *) parsetree->stmt; if (!stmt->ismove) { Portal fportal = GetPortalByName(stmt->portalname); if (PortalIsValid(fportal) && (fportal->cursorOptions & CURSOR_OPT_BINARY)) format = 1; /* 二进制格式;BINARY */ } } PortalSetResultFormat(portal, 1, &format);//设置结果返回的格式,默认为TEXT /* * Now we can create the destination receiver object. * 现在可以创建目标接收器对象了 */ //创建目标接收器(如使用psql则为:printtup DestReceiver) receiver = CreateDestReceiver(dest); if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * Switch back to transaction context for execution. * 切换回原内存上下文 */ MemoryContextSwitchTo(oldcontext);// /* * Run the portal to completion, and then drop it (and the receiver). * 执行PortalRun,然后清除 */ (void) PortalRun(portal, FETCH_ALL, true, /* always top level */ true, receiver, receiver, completionTag);//执行 //执行完毕,销毁接收器 receiver->rDestroy(receiver); //清除Portal中的资源 PortalDrop(portal, false); if (lnext(parsetree_item) == NULL)//所有语句已执行完毕 { /* * If this is the last parsetree of the query string, close down * transaction statement before reporting command-complete. This * is so that any end-of-transaction errors are reported before * the command-complete message is issued, to avoid confusing * clients who will expect either a command-complete message or an * error, not one and then the other. Also, if we're using an * implicit transaction block, we must close that out first. * 如果这是查询字符串的最后一个parsetree,在报告命令完成之前关闭事务语句。 * 这样,在发出命令完整的消息之前就会报告任何事务结束错误,以避免混淆视听, * 因为客户端希望看到命令完整的消息或错误,而不是一个错误,然后是另一个错误。 * 另外,如果我们使用隐式事务块,我们必须首先关闭它。 */ if (use_implicit_block) EndImplicitTransactionBlock();//结束事务 finish_xact_command();//结束事务 } else if (IsA(parsetree->stmt, TransactionStmt))//事务语句?BEGIN/COMMIT/ABORT... { /* * If this was a transaction control statement, commit it. We will * start a new xact command for the next command. * 如果这是一个事务控制语句,提交此语句。 * 我们将为下一个命令启动一个新的xact命令。 */ finish_xact_command(); } else { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. * 在每次查询之后,我们都需要一个CommandCounterIncrement,除了那些启动或结束事务块的查询。 */ CommandCounterIncrement();//命令+1(对应Tuple中的cid) } /* * Tell client that we're done with this query. Note we emit exactly * one EndCommand report for each raw parsetree, thus one for each SQL * command the client sent, regardless of rewriting. (But a command * aborted by error will not send an EndCommand report at all.) * 告诉客户端已经完成了这个查询。注意,对于每个原始的parsetree,只发出一个EndCommand报告, * 因此,对于客户机发送的每个SQL命令,只发出一个EndCommand报告,而不考虑重写。 * (注:由于错误而中止的命令根本不会发送EndCommand报告。) */ EndCommand(completionTag, dest);//命令Done } /* end loop over parsetrees */ //所有语句结束 /* * Close down transaction statement, if one is open. (This will only do * something if the parsetree list was empty; otherwise the last loop * iteration already did it.) * 如果事务是打开的,则关闭。 * (只有当parsetree链表为空时,才会执行某些操作;否则,最后一次循环迭代已经完成了。 */ finish_xact_command(); /* * If there were no parsetrees, return EmptyQueryResponse message. * 如果parsetrees链表已清空,返回EmptyQueryResponse消息 */ if (!parsetree_list) NullCommand(dest); /* * Emit duration logging if appropriate. * 如需要记录日志,则在合适的时候记录 */ switch (check_log_duration(msec_str, was_logged)) { case 1: ereport(LOG, (errmsg("duration: %s ms", msec_str), errhidestmt(true))); break; case 2: ereport(LOG, (errmsg("duration: %s ms statement: %s", msec_str, query_string), errhidestmt(true), errdetail_execute(parsetree_list))); break; } if (save_log_statement_stats) ShowUsage("QUERY STATISTICS"); TRACE_POSTGRESQL_QUERY_DONE(query_string); debug_query_string = NULL;}
到此,关于"PostgreSQL中Review exec_simple_query函数的实现逻辑是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!
事务
命令
语句
查询
分析
上下
上下文
函数
内存
切换
逻辑
信息
消息
状态
错误
元素
快照
报告
目标
学习
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
将图片存入数据库并取出来
网页版登入服务器
ibm服务器主板维修
网络安全案件问题怎么看
北京华百购互联网科技有限公司
gdb数据库怎么直接修改
把手机服务器时间阿里
db2 实例 数据库
深圳博世汽车软件开发
ppt如何画三维数据库
网络安全感知网站
网络安全知识应知应会测试
软件开发生意怎么样
网络安全人才培养与创新峰会
宏杉科技服务器aa
云服务器ecs怎么建站
英雄联盟有没有重庆的服务器
如何与服务器建立安全连接视频
软件产品和软件开发税率
网络安全隔离卡教程
曙光服务器改管理口密码
服务器和带宽
青岛鑫友汇网络技术
芒果数据库安全防护
网络安全技术的课程
电脑做模型用什么软件开发
吃鸡游戏服务器的名字
网上选课软件开发计划
路由器软件开发工具
软件产品和软件开发税率