PostgreSQL在何处处理 sql查询之十

回到 exec_simple_query函数上来。

/*
 * exec_simple_query
 *
 * Execute a "simple Query" protocol message.
 */
static void
exec_simple_query(const char *query_string)
{
    ...
    start_xact_command();
    
... parsetree_list
= pg_parse_query(query_string); ... /* * Run through the raw parsetree(s) and process each one. */ foreach(parsetree_item, parsetree_list) { Node *parsetree = (Node *) lfirst(parsetree_item); ... querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); plantree_list = pg_plan_queries(querytree_list, 0, NULL); /* 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 = CreatePortal("", true, true); /* Don't display the portal in 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. */ PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL); /* * Start the portal. * * If we took a snapshot for parsing/planning, the portal may be able * to reuse it for the execution phase. Currently, this will only * happen in PORTAL_ONE_SELECT mode. But even if PortalStart doesn't * end up being able to do this, keeping the parse/plan snapshot * around until after we start the portal doesn't cost much. */ PortalStart(portal, NULL, 0, snapshot_set); .../* * Run the portal to completion, and then drop it (and the receiver). */ (void) PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag); (*receiver->rDestroy) (receiver); PortalDrop(portal, false); ... EndCommand(completionTag, dest); } /* end loop over parsetrees */ /* * Close down transaction statement, if one is open. */ finish_xact_command(); ... }

通过给表文件强制改名进行调试,可以看到对数据库表文件进行实际的物理访问,发生在 pg_plan_queries  函数执行的时候。

这个动作,早于 PortalStart函数。

如果继续追下去看,是这样的:

/*
 * Generate plans for a list of already-rewritten queries.
 *
 * Normal optimizable statements generate PlannedStmt entries in the result
 * list.  Utility statements are simply represented by their statement nodes.
 */
List *
pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
{
    List       *stmt_list = NIL;
    ListCell   *query_list;

    foreach(query_list, querytrees)
    {
        Query       *query = (Query *) lfirst(query_list);
        Node       *stmt;

        if (query->commandType == CMD_UTILITY)
        {
            /* Utility commands have no plans. */
            stmt = query->utilityStmt;
        }
        else
        {
            stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
        }

        stmt_list = lappend(stmt_list, stmt);
    }

    return stmt_list;
}

上面 的 pg_plan_queries,会调用  pg_plan_query。pg_plan_query的主要执行如下:

/*
 * Generate a plan for a single already-rewritten query.
 * This is a thin wrapper around planner() and takes the same parameters.
 */
PlannedStmt *
pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
{

...

/* call the optimizer */ plan = planner(querytree, cursorOptions, boundParams); ... return plan; }

如果发生对应表名的物理文件发生错误,就会在 planner函数调用时报错。

原文地址:https://www.cnblogs.com/gaojian/p/3094269.html