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 指针。

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