博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PostgreSQL 源码解读(81)- 查询语句#66(Review - exec_simp...
阅读量:2497 次
发布时间:2019-05-11

本文共 14968 字,大约阅读时间需要 49 分钟。

本节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 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;}

二、参考资料

PG Document:Query Planning

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2374809/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/6906/viewspace-2374809/

你可能感兴趣的文章
linux 下eclipse配置apache服务器,选中server时server name为灰色状态
查看>>
实战:ASP.NET MVC中把Views下面的视图放到Views文件夹外
查看>>
发送邮件
查看>>
坏块管理(Bad Block Management,BBM)
查看>>
列表的设计
查看>>
python爬虫---从零开始(五)pyQuery库
查看>>
POJ2236(KB5-A)
查看>>
Centos MySQL数据库迁移详细步骤
查看>>
L2-001. 紧急救援---(Dijkstra,记录路径)
查看>>
2初出茅庐--初级篇2.1
查看>>
新建 WinCE7.0 下的 Silverlight 工程
查看>>
腾讯的张小龙是一个怎样的人?
查看>>
jxl写入excel实现数据导出功能
查看>>
linux文件目录类命令|--cp指令
查看>>
.net MVC 404错误解决方法
查看>>
linux系统目录结构
查看>>
git
查看>>
btn按钮之间事件相互调用
查看>>
Entity Framework 4.3.1 级联删除
查看>>
codevs 1163:访问艺术馆
查看>>