zoukankan      html  css  js  c++  java
  • PostgreSQL在何处处理 sql查询之三十五

    接前面

    深入考察 PortalRun: 初步判断,其核心在于 

    bool
    PortalRun(Portal portal, long count, bool isTopLevel,
              DestReceiver *dest, DestReceiver *altdest,
              char *completionTag)
    {
        ...
        portal->status = PORTAL_ACTIVE;
        ...
        PG_TRY();
        {
            ActivePortal = portal;
            CurrentResourceOwner = portal->resowner;
            PortalContext = PortalGetHeapMemory(portal);
    
            MemoryContextSwitchTo(PortalContext);
    
            switch (portal->strategy)
            {
                case PORTAL_ONE_SELECT:
                case PORTAL_ONE_RETURNING:
                case PORTAL_ONE_MOD_WITH:
                case PORTAL_UTIL_SELECT:
                    ...
                    /*
                     * Now fetch desired portion of results.
                     */
                    nprocessed = PortalRunSelect(portal, true, count, dest);
                    ...
                    break;
    
                case PORTAL_MULTI_QUERY:
                    ...
                    break;
    
                default:
                    ...
                    break;
            }
        }
        PG_CATCH();
        {
            ...
            PG_RE_THROW();
        }
        PG_END_TRY();
        ...
        return result;
    }

    接下来,再接着分析(pgquery.c) PortalRunSelect :

    /*
     * PortalRunSelect
     *        Execute a portal's query in PORTAL_ONE_SELECT mode, and also
     *        when fetching from a completed holdStore in PORTAL_ONE_RETURNING,
     *        PORTAL_ONE_MOD_WITH, and PORTAL_UTIL_SELECT cases.
     *
     * This handles simple N-rows-forward-or-backward cases.  For more complex
     * nonsequential access to a portal, see PortalRunFetch.
     *
     * count <= 0 is interpreted as a no-op: the destination gets started up
     * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
     * interpreted as "all rows".
     *
     * Caller must already have validated the Portal and done appropriate
     * setup (cf. PortalRun).
     *
     * Returns number of rows processed (suitable for use in result tag)
     */
    static long
    PortalRunSelect(Portal portal,
                    bool forward,
                    long count,
                    DestReceiver *dest)
    {
        QueryDesc  *queryDesc;
        ScanDirection direction;
        uint32        nprocessed;
    
        /*
         * NB: queryDesc will be NULL if we are fetching from a held cursor or a
         * completed utility query; can't use it in that path.
         */
        queryDesc = PortalGetQueryDesc(portal);
    
        /* Caller messed up if we have neither a ready query nor held data. */
        Assert(queryDesc || portal->holdStore);
    
        /*
         * Force the queryDesc destination to the right thing.    This supports
         * MOVE, for example, which will pass in dest = DestNone.  This is okay to
         * change as long as we do it on every fetch.  (The Executor must not
         * assume that dest never changes.)
         */
        if (queryDesc)
            queryDesc->dest = dest;
    
        /*
         * Determine which direction to go in, and check to see if we're already
         * at the end of the available tuples in that direction.  If so, set the
         * direction to NoMovement to avoid trying to fetch any tuples.  (This
         * check exists because not all plan node types are robust about being
         * called again if they've already returned NULL once.)  Then call the
         * executor (we must not skip this, because the destination needs to see a
         * setup and shutdown even if no tuples are available).  Finally, update
         * the portal position state depending on the number of tuples that were
         * retrieved.
         */
        if (forward)
        {
            if (portal->atEnd || count <= 0)
                direction = NoMovementScanDirection;
            else
                direction = ForwardScanDirection;
    
            /* In the executor, zero count processes all rows */
            if (count == FETCH_ALL)
                count = 0;
    
            if (portal->holdStore)
                nprocessed = RunFromStore(portal, direction, count, dest);
            else
            {
                PushActiveSnapshot(queryDesc->snapshot);
                ExecutorRun(queryDesc, direction, count);
                nprocessed = queryDesc->estate->es_processed;
                PopActiveSnapshot();
            }
    
            if (!ScanDirectionIsNoMovement(direction))
            {
                long        oldPos;
    
                if (nprocessed > 0)
                    portal->atStart = false;        /* OK to go backward now */
                if (count == 0 ||
                    (unsigned long) nprocessed < (unsigned long) count)
                    portal->atEnd = true;    /* we retrieved 'em all */
                oldPos = portal->portalPos;
                portal->portalPos += nprocessed;
                /* portalPos doesn't advance when we fall off the end */
                if (portal->portalPos < oldPos)
                    portal->posOverflow = true;
            }
        }
        else
        {
            if (portal->cursorOptions & CURSOR_OPT_NO_SCROLL)
                ereport(ERROR,
                        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                         errmsg("cursor can only scan forward"),
                         errhint("Declare it with SCROLL option to enable backward scan.")));
    
            if (portal->atStart || count <= 0)
                direction = NoMovementScanDirection;
            else
                direction = BackwardScanDirection;
    
            /* In the executor, zero count processes all rows */
            if (count == FETCH_ALL)
                count = 0;
    
            if (portal->holdStore)
                nprocessed = RunFromStore(portal, direction, count, dest);
            else
            {
                PushActiveSnapshot(queryDesc->snapshot);
                ExecutorRun(queryDesc, direction, count);
                nprocessed = queryDesc->estate->es_processed;
                PopActiveSnapshot();
            }
    
            if (!ScanDirectionIsNoMovement(direction))
            {
                if (nprocessed > 0 && portal->atEnd)
                {
                    portal->atEnd = false;    /* OK to go forward now */
                    portal->portalPos++;    /* adjust for endpoint case */
                }
                if (count == 0 ||
                    (unsigned long) nprocessed < (unsigned long) count)
                {
                    portal->atStart = true; /* we retrieved 'em all */
                    portal->portalPos = 0;
                    portal->posOverflow = false;
                }
                else
                {
                    long        oldPos;
    
                    oldPos = portal->portalPos;
                    portal->portalPos -= nprocessed;
                    if (portal->portalPos > oldPos ||
                        portal->portalPos <= 0)
                        portal->posOverflow = true;
                }
            }
        }
    
        return nprocessed;
    }

    对 PortalRunSelect,需要进一步进行分析:实际执行的时候,可以知道 forward 是 true。

    所以把上面的程序简化成为这样:

    /*
     * PortalRunSelect
     *        Execute a portal's query in PORTAL_ONE_SELECT mode, and also
     *        when fetching from a completed holdStore in PORTAL_ONE_RETURNING,
     *        PORTAL_ONE_MOD_WITH, and PORTAL_UTIL_SELECT cases.
     *
     * This handles simple N-rows-forward-or-backward cases.  For more complex
     * nonsequential access to a portal, see PortalRunFetch.
     *
     * count <= 0 is interpreted as a no-op: the destination gets started up
     * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
     * interpreted as "all rows".
     *
     * Caller must already have validated the Portal and done appropriate
     * setup (cf. PortalRun).
     *
     * Returns number of rows processed (suitable for use in result tag)
     */
    static long
    PortalRunSelect(Portal portal,
                    bool forward,
                    long count,
                    DestReceiver *dest)
    {
        ...
    
        /*
         * Determine which direction to go in, and check to see if we're already
         * at the end of the available tuples in that direction.  If so, set the
         * direction to NoMovement to avoid trying to fetch any tuples.  (This
         * check exists because not all plan node types are robust about being
         * called again if they've already returned NULL once.)  Then call the
         * executor (we must not skip this, because the destination needs to see a
         * setup and shutdown even if no tuples are available).  Finally, update
         * the portal position state depending on the number of tuples that were
         * retrieved.
         */
        if (forward)
        {
            if (portal->atEnd || count <= 0)
                direction = NoMovementScanDirection;
            else
                direction = ForwardScanDirection;
    
            /* In the executor, zero count processes all rows */
            if (count == FETCH_ALL)
                count = 0;
    
            if (portal->holdStore)
                nprocessed = RunFromStore(portal, direction, count, dest);
            else
            {
                PushActiveSnapshot(queryDesc->snapshot);
                ExecutorRun(queryDesc, direction, count);
                nprocessed = queryDesc->estate->es_processed;
                PopActiveSnapshot();
            }
    
            if (!ScanDirectionIsNoMovement(direction))
            {
                long        oldPos;
    
                if (nprocessed > 0)
                    portal->atStart = false;        /* OK to go backward now */
                if (count == 0 ||
                    (unsigned long) nprocessed < (unsigned long) count)
                    portal->atEnd = true;    /* we retrieved 'em all */
                oldPos = portal->portalPos;
                portal->portalPos += nprocessed;
                /* portalPos doesn't advance when we fall off the end */
                if (portal->portalPos < oldPos)
                    portal->posOverflow = true;
            }
        }
        else
        {
           ...
        }
    
        return nprocessed;
    }

    对于上面的这一段:

            if (portal->atEnd || count <= 0)
                direction = NoMovementScanDirection;
            else
                direction = ForwardScanDirection;

    实际测试,select id from ts t04 获得的是 direction = ForwardScanDirection

    而且,无论是否带有 where 条件,上述代码中都是 (count == FETCH_ALL),也就是最后 count 被赋值为0

    postgres=# select id from tst04;
     id 
    ----
      1
      2
      3
    (3 rows)
    
    postgres=# select id from tst04 where id<3;
     id 
    ----
      1
      2
    (2 rows)
    
    postgres=# 

    下面这段:

            if (portal->holdStore)
                nprocessed = RunFromStore(portal, direction, count, dest);
            else
            {
                PushActiveSnapshot(queryDesc->snapshot);
                ExecutorRun(queryDesc, direction, count);
                nprocessed = queryDesc->estate->es_processed;
                PopActiveSnapshot();
            }

    由于 portal->holdStore 不成立,所以 执行的是 

            {
                PushActiveSnapshot(queryDesc->snapshot);
                ExecutorRun(queryDesc, direction, count);
                nprocessed = queryDesc->estate->es_processed;
                PopActiveSnapshot();
            }

    并且,经过运行发现,下面这段里面的内容,也会执行:

            if (!ScanDirectionIsNoMovement(direction))
            {
                long        oldPos;
    
                if (nprocessed > 0)
                    portal->atStart = false;        /* OK to go backward now */
                if (count == 0 ||
                    (unsigned long) nprocessed < (unsigned long) count)
                    portal->atEnd = true;    /* we retrieved 'em all */
                oldPos = portal->portalPos;
                portal->portalPos += nprocessed;
                /* portalPos doesn't advance when we fall off the end */
                if (portal->portalPos < oldPos)
                    portal->posOverflow = true;
            }

    进一步,将要分析 ExecutorRun(queryDesc, direction, count) 

  • 相关阅读:
    我的开发环境配置经验
    C# WINFORM 打包数据库
    C#格式化数值结果表(格式化字符串)
    Excel如何固定表头,任意一行
    下载fiddler证书并设置信任
    fiddler展示serverIP方法
    fiddler抓包参数乱码的解决方法
    fiddler模拟发送get/post请求(也可做简单接口测试)
    Jenkins常用插件
    关于gitignore文件的创建与使用
  • 原文地址:https://www.cnblogs.com/gaojian/p/3108071.html
Copyright © 2011-2022 走看看