PostgreSQL在何处处理 sql查询之二十九
接前面,继续分析 ChoosePortalStrategy:
/* * ChoosePortalStrategy * Select portal execution strategy given the intended statement list. * * The list elements can be Querys, PlannedStmts, or utility statements. * That's more general than portals need, but plancache.c uses this too. * * See the comments in portal.h. */ 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; if (query->canSetTag) { if (query->commandType == CMD_SELECT && query->utilityStmt == NULL) { if (query->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL) { if (UtilityReturnsTuples(query->utilityStmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } } else if (IsA(stmt, PlannedStmt)) { 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; } } } else { /* must be a utility command; assume it's canSetTag */ if (UtilityReturnsTuples(stmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } /* * 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) { Node *stmt = (Node *) lfirst(lc); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; if (query->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (query->returningList == NIL) return PORTAL_MULTI_QUERY; /* no need to look further */ } } else if (IsA(stmt, PlannedStmt)) { PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (!pstmt->hasReturning) return PORTAL_MULTI_QUERY; /* no need to look further */ } } /* otherwise, utility command, assumed not canSetTag */ } if (nSetTag == 1) return PORTAL_ONE_RETURNING; /* Else, it's the general case... */ return PORTAL_MULTI_QUERY; }
先展开第一段的判断:if (list_length(stmts) == 1)
其实是:
static inline int list_length(const List *l) { return l ? l->length : 0; }
这里我作一个查询验证一下,
select * from tst01 where id IN (select sid from tst02) or id IN (select sid from tst03);
list_length(stmts) == 1 的条件满足。
再看:
#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; }
所以呢,这句 :Node *stmt = (Node *) lfirst(lc); 就是拿到了 计划树的头,并且转换为 Node 指针。