接前面,继续分析:
PortalStrategy ChoosePortalStrategy(List *stmts) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the * single-statement case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. PORTAL_ONE_MOD_WITH * likewise allows only one top-level statement. */ if (list_length(stmts) == 1) { Node *stmt = (Node *) linitial(stmts); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; ... } else if (IsA(stmt, PlannedStmt)) { ... } else { ... } } /* * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and * it has a RETURNING list. */ nSetTag = 0; foreach(lc, stmts) { ... } if (nSetTag == 1) return PORTAL_ONE_RETURNING; /* Else, it's the general case... */ return PORTAL_MULTI_QUERY; }
对 if (IsA(stmt, Query)) 进行分析:
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
#define nodeTag(nodeptr) (((const Node*)(nodeptr))->type)
从上面看到,就是 获得计划树的Head, 把它转为 Node类型指针。
然后看看它的 type是否是 T_Query
经过实际测试,满足条件的是: else if (IsA(stmt, PlannedStmt)),
也就是说 Node指针所指向Node结构的 type是 T_PlannedStmt。
typedef struct Node { NodeTag type; } Node;
typedef struct List { NodeTag type; /* T_List, T_IntList, or T_OidList */ int length; ListCell *head; ListCell *tail; } List; struct ListCell { union { void *ptr_value; int int_value; Oid oid_value; } data; ListCell *next; };
可以说, List 的头是一个Node,内有Nodetag说明其为何种类型:
#define lfirst(lc) ((lc)->data.ptr_value) #define linitial(l) lfirst(list_head(l)) static inline ListCell * list_head(const List *l) { return l ? l->head : NULL; }
stmt再被强制转为 PlannedStmt:
* ---------------- * PlannedStmt node * * The output of the planner is a Plan tree headed by a PlannedStmt node. * PlannedStmt holds the "one time" information needed by the executor. * ---------------- */ typedef struct PlannedStmt { NodeTag type; CmdType commandType; /* select|insert|update|delete */ uint32 queryId; /* query identifier (copied from Query) */ bool hasReturning; /* is it insert|update|delete RETURNING? */ bool hasModifyingCTE; /* has insert|update|delete in WITH? */ bool canSetTag; /* do I set the command result tag? */ bool transientPlan; /* redo plan when TransactionXmin changes? */ struct Plan *planTree; /* tree of Plan nodes */ List *rtable; /* list of RangeTblEntry nodes */ /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ Node *utilityStmt; /* non-null if this is DECLARE CURSOR */ List *subplans; /* Plan trees for SubPlan expressions */ Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */ List *rowMarks; /* a list of PlanRowMark's */ List *relationOids; /* OIDs of relations the plan depends on */ List *invalItems; /* other dependencies, as PlanInvalItems */ int nParamExec; /* number of PARAM_EXEC Params used */ } PlannedStmt;
由此,再来重点看 elseif (IsA(stmt, PlannedStmt)) 判断分支:
else if (IsA(stmt, PlannedStmt)) { fprintf(stderr,"It is a PlannedStmt... by process %d\n",getpid()); PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (pstmt->commandType == CMD_SELECT && pstmt->utilityStmt == NULL) { if (pstmt->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } } }
根据实际运行 select * from tst01 语句,可以得知返回 PORTAL_ONE_SELECT 类型。