- PostgreSQL 源码解读(81)- 查询语句#66(Rev
- PostgreSQL 源码解读(75)- 查询语句#60(Rev
- PostgreSQL 源码解读(74)- 查询语句#59(Rev
- PostgreSQL 源码解读(66)- 查询语句#51(mak
- PostgreSQL 源码解读(27)- 查询语句#12(查询优
- PostgreSQL 源码解读(24)- 查询语句#9(查询重写
- PostgreSQL 源码解读(25)- 查询语句#10(查询优
- PostgreSQL 源码解读(26)- 查询语句#11(查询优
- PostgreSQL 源码解读(17)- 查询语句#2(查询优化
- PostgreSQL 源码解读(30)- 查询语句#15(查询优
本节Review exec_simple_query函数的实现逻辑,该函数响应并处理"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 void
exec_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;
}
网友评论