]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execScan.c
Fix inconsistencies in the code
[postgresql] / src / backend / executor / execScan.c
index 35007cf0cc0ef5a59bc446c7bd5f3ac139f8010f..c0e4a5376c33741022134281200d86e9d0e64039 100644 (file)
@@ -7,12 +7,12 @@
  *       stuff - checking the qualification and projecting the tuple
  *       appropriately.
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.24 2003/08/04 00:43:17 momjian Exp $
+ *       src/backend/executor/execScan.c
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/memutils.h"
 
 
-static bool tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc);
 
+/*
+ * ExecScanFetch -- check interrupts & fetch next potential tuple
+ *
+ * This routine is concerned with substituting a test tuple if we are
+ * inside an EvalPlanQual recheck.  If we aren't, just execute
+ * the access method's next-tuple routine.
+ */
+static inline TupleTableSlot *
+ExecScanFetch(ScanState *node,
+                         ExecScanAccessMtd accessMtd,
+                         ExecScanRecheckMtd recheckMtd)
+{
+       EState     *estate = node->ps.state;
+
+       CHECK_FOR_INTERRUPTS();
+
+       if (estate->es_epqTupleSlot != NULL)
+       {
+               /*
+                * We are inside an EvalPlanQual recheck.  Return the test tuple if
+                * one is available, after rechecking any access-method-specific
+                * conditions.
+                */
+               Index           scanrelid = ((Scan *) node->ps.plan)->scanrelid;
+
+               if (scanrelid == 0)
+               {
+                       TupleTableSlot *slot = node->ss_ScanTupleSlot;
+
+                       /*
+                        * This is a ForeignScan or CustomScan which has pushed down a
+                        * join to the remote side.  The recheck method is responsible not
+                        * only for rechecking the scan/join quals but also for storing
+                        * the correct tuple in the slot.
+                        */
+                       if (!(*recheckMtd) (node, slot))
+                               ExecClearTuple(slot);   /* would not be returned by scan */
+                       return slot;
+               }
+               else if (estate->es_epqTupleSlot[scanrelid - 1] != NULL)
+               {
+                       TupleTableSlot *slot = node->ss_ScanTupleSlot;
+
+                       /* Return empty slot if we already returned a tuple */
+                       if (estate->es_epqScanDone[scanrelid - 1])
+                               return ExecClearTuple(slot);
+                       /* Else mark to remember that we shouldn't return more */
+                       estate->es_epqScanDone[scanrelid - 1] = true;
+
+                       slot = estate->es_epqTupleSlot[scanrelid - 1];
+
+                       /* Return empty slot if we haven't got a test tuple */
+                       if (TupIsNull(slot))
+                               return NULL;
+
+                       /* Check if it meets the access-method conditions */
+                       if (!(*recheckMtd) (node, slot))
+                               return ExecClearTuple(slot);    /* would not be returned by
+                                                                                                * scan */
+
+                       return slot;
+               }
+       }
+
+       /*
+        * Run the node-type-specific access method function to get the next tuple
+        */
+       return (*accessMtd) (node);
+}
 
 /* ----------------------------------------------------------------
  *             ExecScan
  *
  *             Scans the relation using the 'access method' indicated and
- *             returns the next qualifying tuple in the direction specified
- *             in the global variable ExecDirection.
- *             The access method returns the next tuple and execScan() is
+ *             returns the next qualifying tuple.
+ *             The access method returns the next tuple and ExecScan() is
  *             responsible for checking the tuple returned against the qual-clause.
  *
+ *             A 'recheck method' must also be provided that can check an
+ *             arbitrary tuple of the relation against any qual conditions
+ *             that are implemented internal to the access method.
+ *
  *             Conditions:
  *               -- the "cursor" maintained by the AMI is positioned at the tuple
  *                      returned previously.
@@ -45,71 +116,59 @@ static bool tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc);
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecScan(ScanState * node,
-                ExecScanAccessMtd accessMtd)   /* function returning a tuple */
+ExecScan(ScanState *node,
+                ExecScanAccessMtd accessMtd,   /* function returning a tuple */
+                ExecScanRecheckMtd recheckMtd)
 {
-       EState     *estate;
        ExprContext *econtext;
-       List       *qual;
+       ExprState  *qual;
        ProjectionInfo *projInfo;
-       ExprDoneCond isDone;
-       TupleTableSlot *resultSlot;
 
        /*
         * Fetch data from node
         */
-       estate = node->ps.state;
-       econtext = node->ps.ps_ExprContext;
        qual = node->ps.qual;
        projInfo = node->ps.ps_ProjInfo;
+       econtext = node->ps.ps_ExprContext;
+
+       /* interrupt checks are in ExecScanFetch */
 
        /*
-        * Check to see if we're still projecting out tuples from a previous
-        * scan tuple (because there is a function-returning-set in the
-        * projection expressions).  If so, try to project another one.
+        * If we have neither a qual to check nor a projection to do, just skip
+        * all the overhead and return the raw scan tuple.
         */
-       if (node->ps.ps_TupFromTlist)
+       if (!qual && !projInfo)
        {
-               Assert(projInfo);               /* can't get here if not projecting */
-               resultSlot = ExecProject(projInfo, &isDone);
-               if (isDone == ExprMultipleResult)
-                       return resultSlot;
-               /* Done with that source tuple... */
-               node->ps.ps_TupFromTlist = false;
+               ResetExprContext(econtext);
+               return ExecScanFetch(node, accessMtd, recheckMtd);
        }
 
        /*
         * Reset per-tuple memory context to free any expression evaluation
-        * storage allocated in the previous tuple cycle.  Note this can't
-        * happen until we're done projecting out tuples from a scan tuple.
+        * storage allocated in the previous tuple cycle.
         */
        ResetExprContext(econtext);
 
        /*
-        * get a tuple from the access method loop until we obtain a tuple
-        * which passes the qualification.
+        * get a tuple from the access method.  Loop until we obtain a tuple that
+        * passes the qualification.
         */
        for (;;)
        {
                TupleTableSlot *slot;
 
-               CHECK_FOR_INTERRUPTS();
-
-               slot = (*accessMtd) (node);
+               slot = ExecScanFetch(node, accessMtd, recheckMtd);
 
                /*
-                * if the slot returned by the accessMtd contains NULL, then it
-                * means there is nothing more to scan so we just return an empty
-                * slot, being careful to use the projection result slot so it has
-                * correct tupleDesc.
+                * if the slot returned by the accessMtd contains NULL, then it means
+                * there is nothing more to scan so we just return an empty slot,
+                * being careful to use the projection result slot so it has correct
+                * tupleDesc.
                 */
                if (TupIsNull(slot))
                {
                        if (projInfo)
-                               return ExecStoreTuple(NULL,
-                                                                         projInfo->pi_slot,
-                                                                         InvalidBuffer,
-                                                                         true);
+                               return ExecClearTuple(projInfo->pi_state.resultslot);
                        else
                                return slot;
                }
@@ -122,11 +181,11 @@ ExecScan(ScanState * node,
                /*
                 * check that the current tuple satisfies the qual-clause
                 *
-                * check for non-nil qual here to avoid a function call to ExecQual()
-                * when the qual is nil ... saves only a few cycles, but they add
-                * up ...
+                * check for non-null qual here to avoid a function call to ExecQual()
+                * when the qual is null ... saves only a few cycles, but they add up
+                * ...
                 */
-               if (!qual || ExecQual(qual, econtext, false))
+               if (qual == NULL || ExecQual(qual, econtext))
                {
                        /*
                         * Found a satisfactory scan tuple.
@@ -134,17 +193,10 @@ ExecScan(ScanState * node,
                        if (projInfo)
                        {
                                /*
-                                * Form a projection tuple, store it in the result tuple
-                                * slot and return it --- unless we find we can project no
-                                * tuples from this scan tuple, in which case continue
-                                * scan.
+                                * Form a projection tuple, store it in the result tuple slot
+                                * and return it.
                                 */
-                               resultSlot = ExecProject(projInfo, &isDone);
-                               if (isDone != ExprEndResult)
-                               {
-                                       node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-                                       return resultSlot;
-                               }
+                               return ExecProject(projInfo);
                        }
                        else
                        {
@@ -154,6 +206,8 @@ ExecScan(ScanState * node,
                                return slot;
                        }
                }
+               else
+                       InstrCountFiltered1(node, 1);
 
                /*
                 * Tuple fails qual, so free per-tuple memory and try again.
@@ -173,49 +227,76 @@ ExecScan(ScanState * node,
  * the scan node, because the planner will preferentially generate a matching
  * tlist.
  *
- * ExecAssignScanType must have been called already.
+ * The scan slot's descriptor must have been set already.
  */
 void
-ExecAssignScanProjectionInfo(ScanState * node)
+ExecAssignScanProjectionInfo(ScanState *node)
 {
        Scan       *scan = (Scan *) node->ps.plan;
+       TupleDesc       tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
+
+       ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
+}
+
+/*
+ * ExecAssignScanProjectionInfoWithVarno
+ *             As above, but caller can specify varno expected in Vars in the tlist.
+ */
+void
+ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno)
+{
+       TupleDesc       tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
 
-       if (tlist_matches_tupdesc(scan->plan.targetlist,
-                                                         scan->scanrelid,
-                                                       node->ss_ScanTupleSlot->ttc_tupleDescriptor))
-               node->ps.ps_ProjInfo = NULL;
-       else
-               ExecAssignProjectionInfo(&node->ps);
+       ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, varno);
 }
 
-static bool
-tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc)
+/*
+ * ExecScanReScan
+ *
+ * This must be called within the ReScan function of any plan node type
+ * that uses ExecScan().
+ */
+void
+ExecScanReScan(ScanState *node)
 {
-       int                     numattrs = tupdesc->natts;
-       int                     attrno;
+       EState     *estate = node->ps.state;
+
+       /*
+        * We must clear the scan tuple so that observers (e.g., execCurrent.c)
+        * can tell that this plan node is not positioned on a tuple.
+        */
+       ExecClearTuple(node->ss_ScanTupleSlot);
 
-       for (attrno = 1; attrno <= numattrs; attrno++)
+       /* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */
+       if (estate->es_epqScanDone != NULL)
        {
-               Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
-               Var                *var;
-
-               if (tlist == NIL)
-                       return false;           /* tlist too short */
-               var = (Var *) ((TargetEntry *) lfirst(tlist))->expr;
-               if (!var || !IsA(var, Var))
-                       return false;           /* tlist item not a Var */
-               Assert(var->varno == varno);
-               if (var->varattno != attrno)
-                       return false;           /* out of order */
-               Assert(var->vartype == att_tup->atttypid);
-               Assert(var->vartypmod == att_tup->atttypmod);
-               Assert(var->varlevelsup == 0);
-
-               tlist = lnext(tlist);
-       }
+               Index           scanrelid = ((Scan *) node->ps.plan)->scanrelid;
+
+               if (scanrelid > 0)
+                       estate->es_epqScanDone[scanrelid - 1] = false;
+               else
+               {
+                       Bitmapset  *relids;
+                       int                     rtindex = -1;
 
-       if (tlist)
-               return false;                   /* tlist too long */
+                       /*
+                        * If an FDW or custom scan provider has replaced the join with a
+                        * scan, there are multiple RTIs; reset the epqScanDone flag for
+                        * all of them.
+                        */
+                       if (IsA(node->ps.plan, ForeignScan))
+                               relids = ((ForeignScan *) node->ps.plan)->fs_relids;
+                       else if (IsA(node->ps.plan, CustomScan))
+                               relids = ((CustomScan *) node->ps.plan)->custom_relids;
+                       else
+                               elog(ERROR, "unexpected scan node: %d",
+                                        (int) nodeTag(node->ps.plan));
 
-       return true;
+                       while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
+                       {
+                               Assert(rtindex > 0);
+                               estate->es_epqScanDone[rtindex - 1] = false;
+                       }
+               }
+       }
 }