LCOV - code coverage report
Current view: top level - src/backend/executor - nodeLimit.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 109 119 91.6 %
Date: 2017-09-29 15:12:54 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-2017, 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         912 : ExecLimit(PlanState *pstate)
      42             : {
      43         912 :     LimitState *node = castNode(LimitState, pstate);
      44             :     ScanDirection direction;
      45             :     TupleTableSlot *slot;
      46             :     PlanState  *outerPlan;
      47             : 
      48         912 :     CHECK_FOR_INTERRUPTS();
      49             : 
      50             :     /*
      51             :      * get information from the node
      52             :      */
      53         912 :     direction = node->ps.state->es_direction;
      54         912 :     outerPlan = outerPlanState(node);
      55             : 
      56             :     /*
      57             :      * The main logic is a simple state machine.
      58             :      */
      59         912 :     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         180 :             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         290 :             if (!ScanDirectionIsForward(direction))
      79           0 :                 return NULL;
      80             : 
      81             :             /*
      82             :              * Check for empty window; if so, treat like empty subplan.
      83             :              */
      84         290 :             if (node->count <= 0 && !node->noCount)
      85             :             {
      86          22 :                 node->lstate = LIMIT_EMPTY;
      87          22 :                 return NULL;
      88             :             }
      89             : 
      90             :             /*
      91             :              * Fetch rows from subplan until we reach position > offset.
      92             :              */
      93             :             for (;;)
      94             :             {
      95        3329 :                 slot = ExecProcNode(outerPlan);
      96        3329 :                 if (TupIsNull(slot))
      97             :                 {
      98             :                     /*
      99             :                      * The subplan returns too few tuples for us to produce
     100             :                      * any output at all.
     101             :                      */
     102          48 :                     node->lstate = LIMIT_EMPTY;
     103          48 :                     return NULL;
     104             :                 }
     105        3281 :                 node->subSlot = slot;
     106        3281 :                 if (++node->position > node->offset)
     107         220 :                     break;
     108        3061 :             }
     109             : 
     110             :             /*
     111             :              * Okay, we have the first tuple of the window.
     112             :              */
     113         220 :             node->lstate = LIMIT_INWINDOW;
     114         220 :             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         616 :             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        1188 :                 if (!node->noCount &&
     134         582 :                     node->position - node->offset >= node->count)
     135             :                 {
     136         192 :                     node->lstate = LIMIT_WINDOWEND;
     137         192 :                     return NULL;
     138             :                 }
     139             : 
     140             :                 /*
     141             :                  * Get next tuple from subplan, if any.
     142             :                  */
     143         414 :                 slot = ExecProcNode(outerPlan);
     144         414 :                 if (TupIsNull(slot))
     145             :                 {
     146          10 :                     node->lstate = LIMIT_SUBPLANEOF;
     147          10 :                     return NULL;
     148             :                 }
     149         404 :                 node->subSlot = slot;
     150         404 :                 node->position++;
     151             :             }
     152             :             else
     153             :             {
     154             :                 /*
     155             :                  * Backwards scan, so check for stepping off start of window.
     156             :                  * As above, change only state-machine status if so.
     157             :                  */
     158          10 :                 if (node->position <= node->offset + 1)
     159             :                 {
     160           3 :                     node->lstate = LIMIT_WINDOWSTART;
     161           3 :                     return NULL;
     162             :                 }
     163             : 
     164             :                 /*
     165             :                  * Get previous tuple from subplan; there should be one!
     166             :                  */
     167           7 :                 slot = ExecProcNode(outerPlan);
     168           7 :                 if (TupIsNull(slot))
     169           0 :                     elog(ERROR, "LIMIT subplan failed to run backwards");
     170           7 :                 node->subSlot = slot;
     171           7 :                 node->position--;
     172             :             }
     173         411 :             break;
     174             : 
     175             :         case LIMIT_SUBPLANEOF:
     176           2 :             if (ScanDirectionIsForward(direction))
     177           0 :                 return NULL;
     178             : 
     179             :             /*
     180             :              * Backing up from subplan EOF, so re-fetch previous tuple; there
     181             :              * should be one!  Note previous tuple must be in window.
     182             :              */
     183           2 :             slot = ExecProcNode(outerPlan);
     184           2 :             if (TupIsNull(slot))
     185           0 :                 elog(ERROR, "LIMIT subplan failed to run backwards");
     186           2 :             node->subSlot = slot;
     187           2 :             node->lstate = LIMIT_INWINDOW;
     188             :             /* position does not change 'cause we didn't advance it before */
     189           2 :             break;
     190             : 
     191             :         case LIMIT_WINDOWEND:
     192           1 :             if (ScanDirectionIsForward(direction))
     193           0 :                 return NULL;
     194             : 
     195             :             /*
     196             :              * Backing up from window end: simply re-return the last tuple
     197             :              * fetched from the subplan.
     198             :              */
     199           1 :             slot = node->subSlot;
     200           1 :             node->lstate = LIMIT_INWINDOW;
     201             :             /* position does not change 'cause we didn't advance it before */
     202           1 :             break;
     203             : 
     204             :         case LIMIT_WINDOWSTART:
     205           3 :             if (!ScanDirectionIsForward(direction))
     206           0 :                 return NULL;
     207             : 
     208             :             /*
     209             :              * Advancing after having backed off window start: simply
     210             :              * re-return the last tuple fetched from the subplan.
     211             :              */
     212           3 :             slot = node->subSlot;
     213           3 :             node->lstate = LIMIT_INWINDOW;
     214             :             /* position does not change 'cause we didn't change it before */
     215           3 :             break;
     216             : 
     217             :         default:
     218           0 :             elog(ERROR, "impossible LIMIT state: %d",
     219             :                  (int) node->lstate);
     220             :             slot = NULL;        /* keep compiler quiet */
     221             :             break;
     222             :     }
     223             : 
     224             :     /* Return the current tuple */
     225         637 :     Assert(!TupIsNull(slot));
     226             : 
     227         637 :     return slot;
     228             : }
     229             : 
     230             : /*
     231             :  * Evaluate the limit/offset expressions --- done at startup or rescan.
     232             :  *
     233             :  * This is also a handy place to reset the current-position state info.
     234             :  */
     235             : static void
     236         290 : recompute_limits(LimitState *node)
     237             : {
     238         290 :     ExprContext *econtext = node->ps.ps_ExprContext;
     239             :     Datum       val;
     240             :     bool        isNull;
     241             : 
     242         290 :     if (node->limitOffset)
     243             :     {
     244          28 :         val = ExecEvalExprSwitchContext(node->limitOffset,
     245             :                                         econtext,
     246             :                                         &isNull);
     247             :         /* Interpret NULL offset as no offset */
     248          28 :         if (isNull)
     249           1 :             node->offset = 0;
     250             :         else
     251             :         {
     252          27 :             node->offset = DatumGetInt64(val);
     253          27 :             if (node->offset < 0)
     254           0 :                 ereport(ERROR,
     255             :                         (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
     256             :                          errmsg("OFFSET must not be negative")));
     257             :         }
     258             :     }
     259             :     else
     260             :     {
     261             :         /* No OFFSET supplied */
     262         262 :         node->offset = 0;
     263             :     }
     264             : 
     265         290 :     if (node->limitCount)
     266             :     {
     267         286 :         val = ExecEvalExprSwitchContext(node->limitCount,
     268             :                                         econtext,
     269             :                                         &isNull);
     270             :         /* Interpret NULL count as no count (LIMIT ALL) */
     271         286 :         if (isNull)
     272             :         {
     273           1 :             node->count = 0;
     274           1 :             node->noCount = true;
     275             :         }
     276             :         else
     277             :         {
     278         285 :             node->count = DatumGetInt64(val);
     279         285 :             if (node->count < 0)
     280           0 :                 ereport(ERROR,
     281             :                         (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
     282             :                          errmsg("LIMIT must not be negative")));
     283         285 :             node->noCount = false;
     284             :         }
     285             :     }
     286             :     else
     287             :     {
     288             :         /* No COUNT supplied */
     289           4 :         node->count = 0;
     290           4 :         node->noCount = true;
     291             :     }
     292             : 
     293             :     /* Reset position to start-of-scan */
     294         290 :     node->position = 0;
     295         290 :     node->subSlot = NULL;
     296             : 
     297             :     /* Set state-machine state */
     298         290 :     node->lstate = LIMIT_RESCAN;
     299             : 
     300             :     /*
     301             :      * Notify child node about limit.  Note: think not to "optimize" by
     302             :      * skipping ExecSetTupleBound if compute_tuples_needed returns < 0.  We
     303             :      * must update the child node anyway, in case this is a rescan and the
     304             :      * previous time we got a different result.
     305             :      */
     306         290 :     ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node));
     307         290 : }
     308             : 
     309             : /*
     310             :  * Compute the maximum number of tuples needed to satisfy this Limit node.
     311             :  * Return a negative value if there is not a determinable limit.
     312             :  */
     313             : static int64
     314         290 : compute_tuples_needed(LimitState *node)
     315             : {
     316         290 :     if (node->noCount)
     317           5 :         return -1;
     318             :     /* Note: if this overflows, we'll return a negative value, which is OK */
     319         285 :     return node->count + node->offset;
     320             : }
     321             : 
     322             : /* ----------------------------------------------------------------
     323             :  *      ExecInitLimit
     324             :  *
     325             :  *      This initializes the limit node state structures and
     326             :  *      the node's subplan.
     327             :  * ----------------------------------------------------------------
     328             :  */
     329             : LimitState *
     330         260 : ExecInitLimit(Limit *node, EState *estate, int eflags)
     331             : {
     332             :     LimitState *limitstate;
     333             :     Plan       *outerPlan;
     334             : 
     335             :     /* check for unsupported flags */
     336         260 :     Assert(!(eflags & EXEC_FLAG_MARK));
     337             : 
     338             :     /*
     339             :      * create state structure
     340             :      */
     341         260 :     limitstate = makeNode(LimitState);
     342         260 :     limitstate->ps.plan = (Plan *) node;
     343         260 :     limitstate->ps.state = estate;
     344         260 :     limitstate->ps.ExecProcNode = ExecLimit;
     345             : 
     346         260 :     limitstate->lstate = LIMIT_INITIAL;
     347             : 
     348             :     /*
     349             :      * Miscellaneous initialization
     350             :      *
     351             :      * Limit nodes never call ExecQual or ExecProject, but they need an
     352             :      * exprcontext anyway to evaluate the limit/offset parameters in.
     353             :      */
     354         260 :     ExecAssignExprContext(estate, &limitstate->ps);
     355             : 
     356             :     /*
     357             :      * initialize child expressions
     358             :      */
     359         260 :     limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
     360             :                                            (PlanState *) limitstate);
     361         260 :     limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
     362             :                                           (PlanState *) limitstate);
     363             : 
     364             :     /*
     365             :      * Tuple table initialization (XXX not actually used...)
     366             :      */
     367         260 :     ExecInitResultTupleSlot(estate, &limitstate->ps);
     368             : 
     369             :     /*
     370             :      * then initialize outer plan
     371             :      */
     372         260 :     outerPlan = outerPlan(node);
     373         260 :     outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
     374             : 
     375             :     /*
     376             :      * limit nodes do no projections, so initialize projection info for this
     377             :      * node appropriately
     378             :      */
     379         260 :     ExecAssignResultTypeFromTL(&limitstate->ps);
     380         260 :     limitstate->ps.ps_ProjInfo = NULL;
     381             : 
     382         260 :     return limitstate;
     383             : }
     384             : 
     385             : /* ----------------------------------------------------------------
     386             :  *      ExecEndLimit
     387             :  *
     388             :  *      This shuts down the subplan and frees resources allocated
     389             :  *      to this node.
     390             :  * ----------------------------------------------------------------
     391             :  */
     392             : void
     393         254 : ExecEndLimit(LimitState *node)
     394             : {
     395         254 :     ExecFreeExprContext(&node->ps);
     396         254 :     ExecEndNode(outerPlanState(node));
     397         254 : }
     398             : 
     399             : 
     400             : void
     401         110 : ExecReScanLimit(LimitState *node)
     402             : {
     403             :     /*
     404             :      * Recompute limit/offset in case parameters changed, and reset the state
     405             :      * machine.  We must do this before rescanning our child node, in case
     406             :      * it's a Sort that we are passing the parameters down to.
     407             :      */
     408         110 :     recompute_limits(node);
     409             : 
     410             :     /*
     411             :      * if chgParam of subnode is not null then plan will be re-scanned by
     412             :      * first ExecProcNode.
     413             :      */
     414         110 :     if (node->ps.lefttree->chgParam == NULL)
     415          18 :         ExecReScan(node->ps.lefttree);
     416         110 : }

Generated by: LCOV version 1.11