回到 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函数调用时报错。