LCOV - code coverage report
Current view: top level - src/backend/executor - nodeLimit.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 109 119 91.6 %
Date: 2019-09-19 16:06:56 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeLimit.c
       4             :  *    Routines to handle limiting of query results where appropriate
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/executor/nodeLimit.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /*
      16             :  * INTERFACE ROUTINES
      17             :  *      ExecLimit       - extract a limited range of tuples
      18             :  *      ExecInitLimit   - initialize node and subnodes..
      19             :  *      ExecEndLimit    - shutdown node and subnodes
      20             :  */
      21             : 
      22             : #include "postgres.h"
      23             : 
      24             : #include "executor/executor.h"
      25             : #include "executor/nodeLimit.h"
      26             : #include "miscadmin.h"
      27             : #include "nodes/nodeFuncs.h"
      28             : 
      29             : static void recompute_limits(LimitState *node);
      30             : static int64 compute_tuples_needed(LimitState *node);
      31             : 
      32             : 
      33             : /* ----------------------------------------------------------------
      34             :  *      ExecLimit
      35             :  *
      36             :  *      This is a very simple node which just performs LIMIT/OFFSET
      37             :  *      filtering on the stream of tuples returned by a subplan.
      38             :  * ----------------------------------------------------------------
      39             :  */
      40             : static TupleTableSlot *         /* return: a tuple or NULL */
      41        9544 : ExecLimit(PlanState *pstate)
      42             : {
      43        9544 :     LimitState *node = castNode(LimitState, pstate);
      44             :     ScanDirection direction;
      45             :     TupleTableSlot *slot;
      46             :     PlanState  *outerPlan;
      47             : 
      48        9544 :     CHECK_FOR_INTERRUPTS();
      49             : 
      50             :     /*
      51             :      * get information from the node
      52             :      */
      53        9544 :     direction = node->ps.state->es_direction;
      54        9544 :     outerPlan = outerPlanState(node);
      55             : 
      56             :     /*
      57             :      * The main logic is a simple state machine.
      58             :      */
      59        9544 :     switch (node->lstate)
      60             :     {
      61             :         case LIMIT_INITIAL:
      62             : 
      63             :             /*
      64             :              * First call for this node, so compute limit/offset. (We can't do
      65             :              * this any earlier, because parameters from upper nodes will not
      66             :              * be set during ExecInitLimit.)  This also sets position = 0 and
      67             :              * changes the state to LIMIT_RESCAN.
      68             :              */
      69        2464 :             recompute_limits(node);
      70             : 
      71             :             /* FALL THRU */
      72             : 
      73             :         case LIMIT_RESCAN:
      74             : 
      75             :             /*
      76             :              * If backwards scan, just return NULL without changing state.
      77             :              */
      78        2928 :             if (!ScanDirectionIsForward(direction))
      79           0 :                 return NULL;
      80             : 
      81             :             /*
      82             :              * Check for empty window; if so, treat like empty subplan.
      83             :              */
      84        2928 :             if (node->count <= 0 && !node->noCount)
      85             :             {
      86          88 :                 node->lstate = LIMIT_EMPTY;
      87          88 :                 return NULL;
      88             :             }
      89             : 
      90             :             /*
      91             :              * Fetch rows from subplan until we reach position > offset.
      92             :              */
      93             :             for (;;)
      94             :             {
      95       40180 :                 slot = ExecProcNode(outerPlan);
      96       21500 :                 if (TupIsNull(slot))
      97             :                 {
      98             :                     /*
      99             :                      * The subplan returns too few tuples for us to produce
     100             :                      * any output at all.
     101             :                      */
     102         180 :                     node->lstate = LIMIT_EMPTY;
     103         180 :                     return NULL;
     104             :                 }
     105       21320 :                 node->subSlot = slot;
     106       21320 :                 if (++node->position > node->offset)
     107        2650 :                     break;
     108             :             }
     109             : 
     110             :             /*
     111             :              * Okay, we have the first tuple of the window.
     112             :              */
     113        2650 :             node->lstate = LIMIT_INWINDOW;
     114        2650 :             break;
     115             : 
     116             :         case LIMIT_EMPTY:
     117             : 
     118             :             /*
     119             :              * The subplan is known to return no tuples (or not more than
     120             :              * OFFSET tuples, in general).  So we return no tuples.
     121             :              */
     122           0 :             return NULL;
     123             : 
     124             :         case LIMIT_INWINDOW:
     125        6592 :             if (ScanDirectionIsForward(direction))
     126             :             {
     127             :                 /*
     128             :                  * Forwards scan, so check for stepping off end of window. If
     129             :                  * we are at the end of the window, return NULL without
     130             :                  * advancing the subplan or the position variable; but change
     131             :                  * the state machine state to record having done so.
     132             :                  */
     133       13008 :                 if (!node->noCount &&
     134        6456 :                     node->position - node->offset >= node->count)
     135             :                 {
     136        2526 :                     node->lstate = LIMIT_WINDOWEND;
     137             : 
     138             :                     /*
     139             :                      * If we know we won't need to back up, we can release
     140             :                      * resources at this point.
     141             :                      */
     142        2526 :                     if (!(node->ps.state->es_top_eflags & EXEC_FLAG_BACKWARD))
     143        2460 :                         (void) ExecShutdownNode(outerPlan);
     144             : 
     145        2526 :                     return NULL;
     146             :                 }
     147             : 
     148             :                 /*
     149             :                  * Get next tuple from subplan, if any.
     150             :                  */
     151        4026 :                 slot = ExecProcNode(outerPlan);
     152        4022 :                 if (TupIsNull(slot))
     153             :                 {
     154          42 :                     node->lstate = LIMIT_SUBPLANEOF;
     155          42 :                     return NULL;
     156             :                 }
     157        3980 :                 node->subSlot = slot;
     158        3980 :                 node->position++;
     159             :             }
     160             :             else
     161             :             {
     162             :                 /*
     163             :                  * Backwards scan, so check for stepping off start of window.
     164             :                  * As above, change only state-machine status if so.
     165             :                  */
     166          40 :                 if (node->position <= node->offset + 1)
     167             :                 {
     168          12 :                     node->lstate = LIMIT_WINDOWSTART;
     169          12 :                     return NULL;
     170             :                 }
     171             : 
     172             :                 /*
     173             :                  * Get previous tuple from subplan; there should be one!
     174             :                  */
     175          28 :                 slot = ExecProcNode(outerPlan);
     176          28 :                 if (TupIsNull(slot))
     177           0 :                     elog(ERROR, "LIMIT subplan failed to run backwards");
     178          28 :                 node->subSlot = slot;
     179          28 :                 node->position--;
     180             :             }
     181        4008 :             break;
     182             : 
     183             :         case LIMIT_SUBPLANEOF:
     184           8 :             if (ScanDirectionIsForward(direction))
     185           0 :                 return NULL;
     186             : 
     187             :             /*
     188             :              * Backing up from subplan EOF, so re-fetch previous tuple; there
     189             :              * should be one!  Note previous tuple must be in window.
     190             :              */
     191           8 :             slot = ExecProcNode(outerPlan);
     192           8 :             if (TupIsNull(slot))
     193           0 :                 elog(ERROR, "LIMIT subplan failed to run backwards");
     194           8 :             node->subSlot = slot;
     195           8 :             node->lstate = LIMIT_INWINDOW;
     196             :             /* position does not change 'cause we didn't advance it before */
     197           8 :             break;
     198             : 
     199             :         case LIMIT_WINDOWEND:
     200           4 :             if (ScanDirectionIsForward(direction))
     201           0 :                 return NULL;
     202             : 
     203             :             /*
     204             :              * Backing up from window end: simply re-return the last tuple
     205             :              * fetched from the subplan.
     206             :              */
     207           4 :             slot = node->subSlot;
     208           4 :             node->lstate = LIMIT_INWINDOW;
     209             :             /* position does not change 'cause we didn't advance it before */
     210           4 :             break;
     211             : 
     212             :         case LIMIT_WINDOWSTART:
     213          12 :             if (!ScanDirectionIsForward(direction))
     214           0 :                 return NULL;
     215             : 
     216             :             /*
     217             :              * Advancing after having backed off window start: simply
     218             :              * re-return the last tuple fetched from the subplan.
     219             :              */
     220          12 :             slot = node->subSlot;
     221          12 :             node->lstate = LIMIT_INWINDOW;
     222             :             /* position does not change 'cause we didn't change it before */
     223          12 :             break;
     224             : 
     225             :         default:
     226           0 :             elog(ERROR, "impossible LIMIT state: %d",
     227             :                  (int) node->lstate);
     228             :             slot = NULL;        /* keep compiler quiet */
     229             :             break;
     230             :     }
     231             : 
     232             :     /* Return the current tuple */
     233             :     Assert(!TupIsNull(slot));
     234             : 
     235        6682 :     return slot;
     236             : }
     237             : 
     238             : /*
     239             :  * Evaluate the limit/offset expressions --- done at startup or rescan.
     240             :  *
     241             :  * This is also a handy place to reset the current-position state info.
     242             :  */
     243             : static void
     244        2928 : recompute_limits(LimitState *node)
     245             : {
     246        2928 :     ExprContext *econtext = node->ps.ps_ExprContext;
     247             :     Datum       val;
     248             :     bool        isNull;
     249             : 
     250        2928 :     if (node->limitOffset)
     251             :     {
     252         206 :         val = ExecEvalExprSwitchContext(node->limitOffset,
     253             :                                         econtext,
     254             :                                         &isNull);
     255             :         /* Interpret NULL offset as no offset */
     256         206 :         if (isNull)
     257           4 :             node->offset = 0;
     258             :         else
     259             :         {
     260         202 :             node->offset = DatumGetInt64(val);
     261         202 :             if (node->offset < 0)
     262           0 :                 ereport(ERROR,
     263             :                         (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
     264             :                          errmsg("OFFSET must not be negative")));
     265             :         }
     266             :     }
     267             :     else
     268             :     {
     269             :         /* No OFFSET supplied */
     270        2722 :         node->offset = 0;
     271             :     }
     272             : 
     273        2928 :     if (node->limitCount)
     274             :     {
     275        2912 :         val = ExecEvalExprSwitchContext(node->limitCount,
     276             :                                         econtext,
     277             :                                         &isNull);
     278             :         /* Interpret NULL count as no count (LIMIT ALL) */
     279        2912 :         if (isNull)
     280             :         {
     281           4 :             node->count = 0;
     282           4 :             node->noCount = true;
     283             :         }
     284             :         else
     285             :         {
     286        2908 :             node->count = DatumGetInt64(val);
     287        2908 :             if (node->count < 0)
     288           0 :                 ereport(ERROR,
     289             :                         (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
     290             :                          errmsg("LIMIT must not be negative")));
     291        2908 :             node->noCount = false;
     292             :         }
     293             :     }
     294             :     else
     295             :     {
     296             :         /* No COUNT supplied */
     297          16 :         node->count = 0;
     298          16 :         node->noCount = true;
     299             :     }
     300             : 
     301             :     /* Reset position to start-of-scan */
     302        2928 :     node->position = 0;
     303        2928 :     node->subSlot = NULL;
     304             : 
     305             :     /* Set state-machine state */
     306        2928 :     node->lstate = LIMIT_RESCAN;
     307             : 
     308             :     /*
     309             :      * Notify child node about limit.  Note: think not to "optimize" by
     310             :      * skipping ExecSetTupleBound if compute_tuples_needed returns < 0.  We
     311             :      * must update the child node anyway, in case this is a rescan and the
     312             :      * previous time we got a different result.
     313             :      */
     314        2928 :     ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node));
     315        2928 : }
     316             : 
     317             : /*
     318             :  * Compute the maximum number of tuples needed to satisfy this Limit node.
     319             :  * Return a negative value if there is not a determinable limit.
     320             :  */
     321             : static int64
     322        2928 : compute_tuples_needed(LimitState *node)
     323             : {
     324        2928 :     if (node->noCount)
     325          20 :         return -1;
     326             :     /* Note: if this overflows, we'll return a negative value, which is OK */
     327        2908 :     return node->count + node->offset;
     328             : }
     329             : 
     330             : /* ----------------------------------------------------------------
     331             :  *      ExecInitLimit
     332             :  *
     333             :  *      This initializes the limit node state structures and
     334             :  *      the node's subplan.
     335             :  * ----------------------------------------------------------------
     336             :  */
     337             : LimitState *
     338        2958 : ExecInitLimit(Limit *node, EState *estate, int eflags)
     339             : {
     340             :     LimitState *limitstate;
     341             :     Plan       *outerPlan;
     342             : 
     343             :     /* check for unsupported flags */
     344             :     Assert(!(eflags & EXEC_FLAG_MARK));
     345             : 
     346             :     /*
     347             :      * create state structure
     348             :      */
     349        2958 :     limitstate = makeNode(LimitState);
     350        2958 :     limitstate->ps.plan = (Plan *) node;
     351        2958 :     limitstate->ps.state = estate;
     352        2958 :     limitstate->ps.ExecProcNode = ExecLimit;
     353             : 
     354        2958 :     limitstate->lstate = LIMIT_INITIAL;
     355             : 
     356             :     /*
     357             :      * Miscellaneous initialization
     358             :      *
     359             :      * Limit nodes never call ExecQual or ExecProject, but they need an
     360             :      * exprcontext anyway to evaluate the limit/offset parameters in.
     361             :      */
     362        2958 :     ExecAssignExprContext(estate, &limitstate->ps);
     363             : 
     364             :     /*
     365             :      * initialize outer plan
     366             :      */
     367        2958 :     outerPlan = outerPlan(node);
     368        2958 :     outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
     369             : 
     370             :     /*
     371             :      * initialize child expressions
     372             :      */
     373        2958 :     limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
     374             :                                            (PlanState *) limitstate);
     375        2958 :     limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
     376             :                                           (PlanState *) limitstate);
     377             : 
     378             :     /*
     379             :      * Initialize result type.
     380             :      */
     381        2958 :     ExecInitResultTypeTL(&limitstate->ps);
     382             : 
     383        2958 :     limitstate->ps.resultopsset = true;
     384        2958 :     limitstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(limitstate),
     385             :                                                     &limitstate->ps.resultopsfixed);
     386             : 
     387             :     /*
     388             :      * limit nodes do no projections, so initialize projection info for this
     389             :      * node appropriately
     390             :      */
     391        2958 :     limitstate->ps.ps_ProjInfo = NULL;
     392             : 
     393        2958 :     return limitstate;
     394             : }
     395             : 
     396             : /* ----------------------------------------------------------------
     397             :  *      ExecEndLimit
     398             :  *
     399             :  *      This shuts down the subplan and frees resources allocated
     400             :  *      to this node.
     401             :  * ----------------------------------------------------------------
     402             :  */
     403             : void
     404        2920 : ExecEndLimit(LimitState *node)
     405             : {
     406        2920 :     ExecFreeExprContext(&node->ps);
     407        2920 :     ExecEndNode(outerPlanState(node));
     408        2920 : }
     409             : 
     410             : 
     411             : void
     412         464 : ExecReScanLimit(LimitState *node)
     413             : {
     414             :     /*
     415             :      * Recompute limit/offset in case parameters changed, and reset the state
     416             :      * machine.  We must do this before rescanning our child node, in case
     417             :      * it's a Sort that we are passing the parameters down to.
     418             :      */
     419         464 :     recompute_limits(node);
     420             : 
     421             :     /*
     422             :      * if chgParam of subnode is not null then plan will be re-scanned by
     423             :      * first ExecProcNode.
     424             :      */
     425         464 :     if (node->ps.lefttree->chgParam == NULL)
     426          72 :         ExecReScan(node->ps.lefttree);
     427         464 : }

Generated by: LCOV version 1.13