Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

execScan.c

/*-------------------------------------------------------------------------
 *
 * execScan.c
 *      This code provides support for generalized relation scans. ExecScan
 *      is passed a node and a pointer to a function to "do the right thing"
 *      and return a tuple from the relation. ExecScan then does the tedious
 *      stuff - checking the qualification and projecting the tuple
 *      appropriately.
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.46 2009/04/02 20:59:10 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/executor.h"
#include "miscadmin.h"
#include "utils/memutils.h"


static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);


/* ----------------------------------------------------------------
 *          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
 *          responsible for checking the tuple returned against the qual-clause.
 *
 *          Conditions:
 *            -- the "cursor" maintained by the AMI is positioned at the tuple
 *                 returned previously.
 *
 *          Initial States:
 *            -- the relation indicated is opened for scanning so that the
 *                 "cursor" is positioned before the first qualifying tuple.
 * ----------------------------------------------------------------
 */
TupleTableSlot *
ExecScan(ScanState *node,
             ExecScanAccessMtd accessMtd) /* function returning a tuple */
{
      ExprContext *econtext;
      List     *qual;
      ProjectionInfo *projInfo;
      ExprDoneCond isDone;
      TupleTableSlot *resultSlot;

      /*
       * Fetch data from node
       */
      qual = node->ps.qual;
      projInfo = node->ps.ps_ProjInfo;

      /*
       * 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 (!qual && !projInfo)
            return (*accessMtd) (node);

      /*
       * 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 (node->ps.ps_TupFromTlist)
      {
            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;
      }

      /*
       * 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.
       */
      econtext = node->ps.ps_ExprContext;
      ResetExprContext(econtext);

      /*
       * get a tuple from the access method loop until we obtain a tuple which
       * passes the qualification.
       */
      for (;;)
      {
            TupleTableSlot *slot;

            CHECK_FOR_INTERRUPTS();

            slot = (*accessMtd) (node);

            /*
             * 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 ExecClearTuple(projInfo->pi_slot);
                  else
                        return slot;
            }

            /*
             * place the current tuple into the expr context
             */
            econtext->ecxt_scantuple = slot;

            /*
             * 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
             * ...
             */
            if (!qual || ExecQual(qual, econtext, false))
            {
                  /*
                   * Found a satisfactory scan tuple.
                   */
                  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.
                         */
                        resultSlot = ExecProject(projInfo, &isDone);
                        if (isDone != ExprEndResult)
                        {
                              node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
                              return resultSlot;
                        }
                  }
                  else
                  {
                        /*
                         * Here, we aren't projecting, so just return scan tuple.
                         */
                        return slot;
                  }
            }

            /*
             * Tuple fails qual, so free per-tuple memory and try again.
             */
            ResetExprContext(econtext);
      }
}

/*
 * ExecAssignScanProjectionInfo
 *          Set up projection info for a scan node, if necessary.
 *
 * We can avoid a projection step if the requested tlist exactly matches
 * the underlying tuple type.  If so, we just set ps_ProjInfo to NULL.
 * Note that this case occurs not only for simple "SELECT * FROM ...", but
 * also in most cases where there are joins or other processing nodes above
 * the scan node, because the planner will preferentially generate a matching
 * tlist.
 *
 * ExecAssignScanType must have been called already.
 */
void
ExecAssignScanProjectionInfo(ScanState *node)
{
      Scan     *scan = (Scan *) node->ps.plan;

      if (tlist_matches_tupdesc(&node->ps,
                                            scan->plan.targetlist,
                                            scan->scanrelid,
                                            node->ss_ScanTupleSlot->tts_tupleDescriptor))
            node->ps.ps_ProjInfo = NULL;
      else
            ExecAssignProjectionInfo(&node->ps,
                                                 node->ss_ScanTupleSlot->tts_tupleDescriptor);
}

static bool
tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
{
      int               numattrs = tupdesc->natts;
      int               attrno;
      bool        hasoid;
      ListCell   *tlist_item = list_head(tlist);

      /* Check the tlist attributes */
      for (attrno = 1; attrno <= numattrs; attrno++)
      {
            Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
            Var            *var;

            if (tlist_item == NULL)
                  return false;           /* tlist too short */
            var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
            if (!var || !IsA(var, Var))
                  return false;           /* tlist item not a Var */
            /* if these Asserts fail, planner messed up */
            Assert(var->varno == varno);
            Assert(var->varlevelsup == 0);
            if (var->varattno != attrno)
                  return false;           /* out of order */
            if (att_tup->attisdropped)
                  return false;           /* table contains dropped columns */

            /*
             * Note: usually the Var's type should match the tupdesc exactly, but
             * in situations involving unions of columns that have different
             * typmods, the Var may have come from above the union and hence have
             * typmod -1.  This is a legitimate situation since the Var still
             * describes the column, just not as exactly as the tupdesc does. We
             * could change the planner to prevent it, but it'd then insert
             * projection steps just to convert from specific typmod to typmod -1,
             * which is pretty silly.
             */
            if (var->vartype != att_tup->atttypid ||
                  (var->vartypmod != att_tup->atttypmod &&
                   var->vartypmod != -1))
                  return false;           /* type mismatch */

            tlist_item = lnext(tlist_item);
      }

      if (tlist_item)
            return false;                 /* tlist too long */

      /*
       * If the plan context requires a particular hasoid setting, then that has
       * to match, too.
       */
      if (ExecContextForcesOids(ps, &hasoid) &&
            hasoid != tupdesc->tdhasoid)
            return false;

      return true;
}

Generated by  Doxygen 1.6.0   Back to index