Header And Logo

PostgreSQL
| The world's most advanced open source database.

nodeLimit.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * nodeLimit.c
00004  *    Routines to handle limiting of query results where appropriate
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/executor/nodeLimit.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 /*
00016  * INTERFACE ROUTINES
00017  *      ExecLimit       - extract a limited range of tuples
00018  *      ExecInitLimit   - initialize node and subnodes..
00019  *      ExecEndLimit    - shutdown node and subnodes
00020  */
00021 
00022 #include "postgres.h"
00023 
00024 #include "executor/executor.h"
00025 #include "executor/nodeLimit.h"
00026 #include "nodes/nodeFuncs.h"
00027 
00028 static void recompute_limits(LimitState *node);
00029 static void pass_down_bound(LimitState *node, PlanState *child_node);
00030 
00031 
00032 /* ----------------------------------------------------------------
00033  *      ExecLimit
00034  *
00035  *      This is a very simple node which just performs LIMIT/OFFSET
00036  *      filtering on the stream of tuples returned by a subplan.
00037  * ----------------------------------------------------------------
00038  */
00039 TupleTableSlot *                /* return: a tuple or NULL */
00040 ExecLimit(LimitState *node)
00041 {
00042     ScanDirection direction;
00043     TupleTableSlot *slot;
00044     PlanState  *outerPlan;
00045 
00046     /*
00047      * get information from the node
00048      */
00049     direction = node->ps.state->es_direction;
00050     outerPlan = outerPlanState(node);
00051 
00052     /*
00053      * The main logic is a simple state machine.
00054      */
00055     switch (node->lstate)
00056     {
00057         case LIMIT_INITIAL:
00058 
00059             /*
00060              * First call for this node, so compute limit/offset. (We can't do
00061              * this any earlier, because parameters from upper nodes will not
00062              * be set during ExecInitLimit.)  This also sets position = 0 and
00063              * changes the state to LIMIT_RESCAN.
00064              */
00065             recompute_limits(node);
00066 
00067             /* FALL THRU */
00068 
00069         case LIMIT_RESCAN:
00070 
00071             /*
00072              * If backwards scan, just return NULL without changing state.
00073              */
00074             if (!ScanDirectionIsForward(direction))
00075                 return NULL;
00076 
00077             /*
00078              * Check for empty window; if so, treat like empty subplan.
00079              */
00080             if (node->count <= 0 && !node->noCount)
00081             {
00082                 node->lstate = LIMIT_EMPTY;
00083                 return NULL;
00084             }
00085 
00086             /*
00087              * Fetch rows from subplan until we reach position > offset.
00088              */
00089             for (;;)
00090             {
00091                 slot = ExecProcNode(outerPlan);
00092                 if (TupIsNull(slot))
00093                 {
00094                     /*
00095                      * The subplan returns too few tuples for us to produce
00096                      * any output at all.
00097                      */
00098                     node->lstate = LIMIT_EMPTY;
00099                     return NULL;
00100                 }
00101                 node->subSlot = slot;
00102                 if (++node->position > node->offset)
00103                     break;
00104             }
00105 
00106             /*
00107              * Okay, we have the first tuple of the window.
00108              */
00109             node->lstate = LIMIT_INWINDOW;
00110             break;
00111 
00112         case LIMIT_EMPTY:
00113 
00114             /*
00115              * The subplan is known to return no tuples (or not more than
00116              * OFFSET tuples, in general).  So we return no tuples.
00117              */
00118             return NULL;
00119 
00120         case LIMIT_INWINDOW:
00121             if (ScanDirectionIsForward(direction))
00122             {
00123                 /*
00124                  * Forwards scan, so check for stepping off end of window. If
00125                  * we are at the end of the window, return NULL without
00126                  * advancing the subplan or the position variable; but change
00127                  * the state machine state to record having done so.
00128                  */
00129                 if (!node->noCount &&
00130                     node->position - node->offset >= node->count)
00131                 {
00132                     node->lstate = LIMIT_WINDOWEND;
00133                     return NULL;
00134                 }
00135 
00136                 /*
00137                  * Get next tuple from subplan, if any.
00138                  */
00139                 slot = ExecProcNode(outerPlan);
00140                 if (TupIsNull(slot))
00141                 {
00142                     node->lstate = LIMIT_SUBPLANEOF;
00143                     return NULL;
00144                 }
00145                 node->subSlot = slot;
00146                 node->position++;
00147             }
00148             else
00149             {
00150                 /*
00151                  * Backwards scan, so check for stepping off start of window.
00152                  * As above, change only state-machine status if so.
00153                  */
00154                 if (node->position <= node->offset + 1)
00155                 {
00156                     node->lstate = LIMIT_WINDOWSTART;
00157                     return NULL;
00158                 }
00159 
00160                 /*
00161                  * Get previous tuple from subplan; there should be one!
00162                  */
00163                 slot = ExecProcNode(outerPlan);
00164                 if (TupIsNull(slot))
00165                     elog(ERROR, "LIMIT subplan failed to run backwards");
00166                 node->subSlot = slot;
00167                 node->position--;
00168             }
00169             break;
00170 
00171         case LIMIT_SUBPLANEOF:
00172             if (ScanDirectionIsForward(direction))
00173                 return NULL;
00174 
00175             /*
00176              * Backing up from subplan EOF, so re-fetch previous tuple; there
00177              * should be one!  Note previous tuple must be in window.
00178              */
00179             slot = ExecProcNode(outerPlan);
00180             if (TupIsNull(slot))
00181                 elog(ERROR, "LIMIT subplan failed to run backwards");
00182             node->subSlot = slot;
00183             node->lstate = LIMIT_INWINDOW;
00184             /* position does not change 'cause we didn't advance it before */
00185             break;
00186 
00187         case LIMIT_WINDOWEND:
00188             if (ScanDirectionIsForward(direction))
00189                 return NULL;
00190 
00191             /*
00192              * Backing up from window end: simply re-return the last tuple
00193              * fetched from the subplan.
00194              */
00195             slot = node->subSlot;
00196             node->lstate = LIMIT_INWINDOW;
00197             /* position does not change 'cause we didn't advance it before */
00198             break;
00199 
00200         case LIMIT_WINDOWSTART:
00201             if (!ScanDirectionIsForward(direction))
00202                 return NULL;
00203 
00204             /*
00205              * Advancing after having backed off window start: simply
00206              * re-return the last tuple fetched from the subplan.
00207              */
00208             slot = node->subSlot;
00209             node->lstate = LIMIT_INWINDOW;
00210             /* position does not change 'cause we didn't change it before */
00211             break;
00212 
00213         default:
00214             elog(ERROR, "impossible LIMIT state: %d",
00215                  (int) node->lstate);
00216             slot = NULL;        /* keep compiler quiet */
00217             break;
00218     }
00219 
00220     /* Return the current tuple */
00221     Assert(!TupIsNull(slot));
00222 
00223     return slot;
00224 }
00225 
00226 /*
00227  * Evaluate the limit/offset expressions --- done at startup or rescan.
00228  *
00229  * This is also a handy place to reset the current-position state info.
00230  */
00231 static void
00232 recompute_limits(LimitState *node)
00233 {
00234     ExprContext *econtext = node->ps.ps_ExprContext;
00235     Datum       val;
00236     bool        isNull;
00237 
00238     if (node->limitOffset)
00239     {
00240         val = ExecEvalExprSwitchContext(node->limitOffset,
00241                                         econtext,
00242                                         &isNull,
00243                                         NULL);
00244         /* Interpret NULL offset as no offset */
00245         if (isNull)
00246             node->offset = 0;
00247         else
00248         {
00249             node->offset = DatumGetInt64(val);
00250             if (node->offset < 0)
00251                 ereport(ERROR,
00252                  (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
00253                   errmsg("OFFSET must not be negative")));
00254         }
00255     }
00256     else
00257     {
00258         /* No OFFSET supplied */
00259         node->offset = 0;
00260     }
00261 
00262     if (node->limitCount)
00263     {
00264         val = ExecEvalExprSwitchContext(node->limitCount,
00265                                         econtext,
00266                                         &isNull,
00267                                         NULL);
00268         /* Interpret NULL count as no count (LIMIT ALL) */
00269         if (isNull)
00270         {
00271             node->count = 0;
00272             node->noCount = true;
00273         }
00274         else
00275         {
00276             node->count = DatumGetInt64(val);
00277             if (node->count < 0)
00278                 ereport(ERROR,
00279                         (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
00280                          errmsg("LIMIT must not be negative")));
00281             node->noCount = false;
00282         }
00283     }
00284     else
00285     {
00286         /* No COUNT supplied */
00287         node->count = 0;
00288         node->noCount = true;
00289     }
00290 
00291     /* Reset position to start-of-scan */
00292     node->position = 0;
00293     node->subSlot = NULL;
00294 
00295     /* Set state-machine state */
00296     node->lstate = LIMIT_RESCAN;
00297 
00298     /* Notify child node about limit, if useful */
00299     pass_down_bound(node, outerPlanState(node));
00300 }
00301 
00302 /*
00303  * If we have a COUNT, and our input is a Sort node, notify it that it can
00304  * use bounded sort.  Also, if our input is a MergeAppend, we can apply the
00305  * same bound to any Sorts that are direct children of the MergeAppend,
00306  * since the MergeAppend surely need read no more than that many tuples from
00307  * any one input.  We also have to be prepared to look through a Result,
00308  * since the planner might stick one atop MergeAppend for projection purposes.
00309  *
00310  * This is a bit of a kluge, but we don't have any more-abstract way of
00311  * communicating between the two nodes; and it doesn't seem worth trying
00312  * to invent one without some more examples of special communication needs.
00313  *
00314  * Note: it is the responsibility of nodeSort.c to react properly to
00315  * changes of these parameters.  If we ever do redesign this, it'd be a
00316  * good idea to integrate this signaling with the parameter-change mechanism.
00317  */
00318 static void
00319 pass_down_bound(LimitState *node, PlanState *child_node)
00320 {
00321     if (IsA(child_node, SortState))
00322     {
00323         SortState  *sortState = (SortState *) child_node;
00324         int64       tuples_needed = node->count + node->offset;
00325 
00326         /* negative test checks for overflow in sum */
00327         if (node->noCount || tuples_needed < 0)
00328         {
00329             /* make sure flag gets reset if needed upon rescan */
00330             sortState->bounded = false;
00331         }
00332         else
00333         {
00334             sortState->bounded = true;
00335             sortState->bound = tuples_needed;
00336         }
00337     }
00338     else if (IsA(child_node, MergeAppendState))
00339     {
00340         MergeAppendState *maState = (MergeAppendState *) child_node;
00341         int         i;
00342 
00343         for (i = 0; i < maState->ms_nplans; i++)
00344             pass_down_bound(node, maState->mergeplans[i]);
00345     }
00346     else if (IsA(child_node, ResultState))
00347     {
00348         /*
00349          * An extra consideration here is that if the Result is projecting a
00350          * targetlist that contains any SRFs, we can't assume that every input
00351          * tuple generates an output tuple, so a Sort underneath might need to
00352          * return more than N tuples to satisfy LIMIT N. So we cannot use
00353          * bounded sort.
00354          *
00355          * If Result supported qual checking, we'd have to punt on seeing a
00356          * qual, too.  Note that having a resconstantqual is not a
00357          * showstopper: if that fails we're not getting any rows at all.
00358          */
00359         if (outerPlanState(child_node) &&
00360             !expression_returns_set((Node *) child_node->plan->targetlist))
00361             pass_down_bound(node, outerPlanState(child_node));
00362     }
00363 }
00364 
00365 /* ----------------------------------------------------------------
00366  *      ExecInitLimit
00367  *
00368  *      This initializes the limit node state structures and
00369  *      the node's subplan.
00370  * ----------------------------------------------------------------
00371  */
00372 LimitState *
00373 ExecInitLimit(Limit *node, EState *estate, int eflags)
00374 {
00375     LimitState *limitstate;
00376     Plan       *outerPlan;
00377 
00378     /* check for unsupported flags */
00379     Assert(!(eflags & EXEC_FLAG_MARK));
00380 
00381     /*
00382      * create state structure
00383      */
00384     limitstate = makeNode(LimitState);
00385     limitstate->ps.plan = (Plan *) node;
00386     limitstate->ps.state = estate;
00387 
00388     limitstate->lstate = LIMIT_INITIAL;
00389 
00390     /*
00391      * Miscellaneous initialization
00392      *
00393      * Limit nodes never call ExecQual or ExecProject, but they need an
00394      * exprcontext anyway to evaluate the limit/offset parameters in.
00395      */
00396     ExecAssignExprContext(estate, &limitstate->ps);
00397 
00398     /*
00399      * initialize child expressions
00400      */
00401     limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
00402                                            (PlanState *) limitstate);
00403     limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
00404                                           (PlanState *) limitstate);
00405 
00406     /*
00407      * Tuple table initialization (XXX not actually used...)
00408      */
00409     ExecInitResultTupleSlot(estate, &limitstate->ps);
00410 
00411     /*
00412      * then initialize outer plan
00413      */
00414     outerPlan = outerPlan(node);
00415     outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
00416 
00417     /*
00418      * limit nodes do no projections, so initialize projection info for this
00419      * node appropriately
00420      */
00421     ExecAssignResultTypeFromTL(&limitstate->ps);
00422     limitstate->ps.ps_ProjInfo = NULL;
00423 
00424     return limitstate;
00425 }
00426 
00427 /* ----------------------------------------------------------------
00428  *      ExecEndLimit
00429  *
00430  *      This shuts down the subplan and frees resources allocated
00431  *      to this node.
00432  * ----------------------------------------------------------------
00433  */
00434 void
00435 ExecEndLimit(LimitState *node)
00436 {
00437     ExecFreeExprContext(&node->ps);
00438     ExecEndNode(outerPlanState(node));
00439 }
00440 
00441 
00442 void
00443 ExecReScanLimit(LimitState *node)
00444 {
00445     /*
00446      * Recompute limit/offset in case parameters changed, and reset the state
00447      * machine.  We must do this before rescanning our child node, in case
00448      * it's a Sort that we are passing the parameters down to.
00449      */
00450     recompute_limits(node);
00451 
00452     /*
00453      * if chgParam of subnode is not null then plan will be re-scanned by
00454      * first ExecProcNode.
00455      */
00456     if (node->ps.lefttree->chgParam == NULL)
00457         ExecReScan(node->ps.lefttree);
00458 }