]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeTidscan.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeTidscan.c
index 6a9e7e5e0701d99e2edd3059c9d20dbb6f6d752c..8daf09c785a5e30fce6d69ee113bdb953f253f4a 100644 (file)
  * nodeTidscan.c
  *       Routines to support direct tid scans of relations
  *
- * Copyright (c) 1994, Regents of the University of California
+ * 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/nodeTidscan.c,v 1.2 1999/12/16 22:19:44 wieck Exp $
+ *       src/backend/executor/nodeTidscan.c
  *
  *-------------------------------------------------------------------------
  */
 /*
  * INTERFACE ROUTINES
  *
- *             ExecTidScan             scans a relation using tids
+ *             ExecTidScan                     scans a relation using tids
  *             ExecInitTidScan         creates and initializes state info.
- *             ExecTidReScan           rescans the tid relation.
+ *             ExecReScanTidScan       rescans the tid relation.
  *             ExecEndTidScan          releases all storage.
- *             ExecTidMarkPos          marks scan position.
- *             ExecTidRestrPos         restores scan position.
- *
  */
 #include "postgres.h"
 
-#include "executor/executor.h"
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/pg_type.h"
 #include "executor/execdebug.h"
 #include "executor/nodeTidscan.h"
-#include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */
-#include "access/heapam.h"
-#include "parser/parsetree.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "storage/bufmgr.h"
+#include "utils/array.h"
+#include "utils/rel.h"
 
-static int TidListCreate(List *, ExprContext *, ItemPointer *);
-static TupleTableSlot *TidNext(TidScan *node);
 
-static int
-TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
+#define IsCTIDVar(node)  \
+       ((node) != NULL && \
+        IsA((node), Var) && \
+        ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
+        ((Var *) (node))->varlevelsup == 0)
+
+/* one element in tss_tidexprs */
+typedef struct TidExpr
 {
-       List            *lst;
-       ItemPointer     itemptr;
-       bool            isNull;
-       int             numTids = 0;
+       ExprState  *exprstate;          /* ExprState for a TID-yielding subexpr */
+       bool            isarray;                /* if true, it yields tid[] not just tid */
+       CurrentOfExpr *cexpr;           /* alternatively, we can have CURRENT OF */
+} TidExpr;
+
+static void TidExprListCreate(TidScanState *tidstate);
+static void TidListEval(TidScanState *tidstate);
+static int     itemptr_comparator(const void *a, const void *b);
+static TupleTableSlot *TidNext(TidScanState *node);
+
+
+/*
+ * Extract the qual subexpressions that yield TIDs to search for,
+ * and compile them into ExprStates if they're ordinary expressions.
+ *
+ * CURRENT OF is a special case that we can't compile usefully;
+ * just drop it into the TidExpr list as-is.
+ */
+static void
+TidExprListCreate(TidScanState *tidstate)
+{
+       TidScan    *node = (TidScan *) tidstate->ss.ps.plan;
+       ListCell   *l;
+
+       tidstate->tss_tidexprs = NIL;
+       tidstate->tss_isCurrentOf = false;
 
-       foreach (lst, evalList)
+       foreach(l, node->tidquals)
        {
-               itemptr = (ItemPointer)ExecEvalExpr(lfirst(lst), econtext,
-                               &isNull, (bool *)0);
-               if (itemptr && ItemPointerIsValid(itemptr))
+               Expr       *expr = (Expr *) lfirst(l);
+               TidExpr    *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
+
+               if (is_opclause(expr))
+               {
+                       Node       *arg1;
+                       Node       *arg2;
+
+                       arg1 = get_leftop(expr);
+                       arg2 = get_rightop(expr);
+                       if (IsCTIDVar(arg1))
+                               tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
+                                                                                                 &tidstate->ss.ps);
+                       else if (IsCTIDVar(arg2))
+                               tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
+                                                                                                 &tidstate->ss.ps);
+                       else
+                               elog(ERROR, "could not identify CTID variable");
+                       tidexpr->isarray = false;
+               }
+               else if (expr && IsA(expr, ScalarArrayOpExpr))
+               {
+                       ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+
+                       Assert(IsCTIDVar(linitial(saex->args)));
+                       tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
+                                                                                         &tidstate->ss.ps);
+                       tidexpr->isarray = true;
+               }
+               else if (expr && IsA(expr, CurrentOfExpr))
                {
-                       tidList[numTids] = itemptr;
-                       numTids++;
+                       CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
+
+                       tidexpr->cexpr = cexpr;
+                       tidstate->tss_isCurrentOf = true;
                }
+               else
+                       elog(ERROR, "could not identify CTID expression");
+
+               tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
        }
-       return numTids;
+
+       /* CurrentOfExpr could never appear OR'd with something else */
+       Assert(list_length(tidstate->tss_tidexprs) == 1 ||
+                  !tidstate->tss_isCurrentOf);
+}
+
+/*
+ * Compute the list of TIDs to be visited, by evaluating the expressions
+ * for them.
+ *
+ * (The result is actually an array, not a list.)
+ */
+static void
+TidListEval(TidScanState *tidstate)
+{
+       ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
+       BlockNumber nblocks;
+       ItemPointerData *tidList;
+       int                     numAllocTids;
+       int                     numTids;
+       ListCell   *l;
+
+       /*
+        * We silently discard any TIDs that are out of range at the time of scan
+        * start.  (Since we hold at least AccessShareLock on the table, it won't
+        * be possible for someone to truncate away the blocks we intend to
+        * visit.)
+        */
+       nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation);
+
+       /*
+        * We initialize the array with enough slots for the case that all quals
+        * are simple OpExprs or CurrentOfExprs.  If there are any
+        * ScalarArrayOpExprs, we may have to enlarge the array.
+        */
+       numAllocTids = list_length(tidstate->tss_tidexprs);
+       tidList = (ItemPointerData *)
+               palloc(numAllocTids * sizeof(ItemPointerData));
+       numTids = 0;
+
+       foreach(l, tidstate->tss_tidexprs)
+       {
+               TidExpr    *tidexpr = (TidExpr *) lfirst(l);
+               ItemPointer itemptr;
+               bool            isNull;
+
+               if (tidexpr->exprstate && !tidexpr->isarray)
+               {
+                       itemptr = (ItemPointer)
+                               DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
+                                                                                                                 econtext,
+                                                                                                                 &isNull));
+                       if (!isNull &&
+                               ItemPointerIsValid(itemptr) &&
+                               ItemPointerGetBlockNumber(itemptr) < nblocks)
+                       {
+                               if (numTids >= numAllocTids)
+                               {
+                                       numAllocTids *= 2;
+                                       tidList = (ItemPointerData *)
+                                               repalloc(tidList,
+                                                                numAllocTids * sizeof(ItemPointerData));
+                               }
+                               tidList[numTids++] = *itemptr;
+                       }
+               }
+               else if (tidexpr->exprstate && tidexpr->isarray)
+               {
+                       Datum           arraydatum;
+                       ArrayType  *itemarray;
+                       Datum      *ipdatums;
+                       bool       *ipnulls;
+                       int                     ndatums;
+                       int                     i;
+
+                       arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
+                                                                                                  econtext,
+                                                                                                  &isNull);
+                       if (isNull)
+                               continue;
+                       itemarray = DatumGetArrayTypeP(arraydatum);
+                       deconstruct_array(itemarray,
+                                                         TIDOID, sizeof(ItemPointerData), false, 's',
+                                                         &ipdatums, &ipnulls, &ndatums);
+                       if (numTids + ndatums > numAllocTids)
+                       {
+                               numAllocTids = numTids + ndatums;
+                               tidList = (ItemPointerData *)
+                                       repalloc(tidList,
+                                                        numAllocTids * sizeof(ItemPointerData));
+                       }
+                       for (i = 0; i < ndatums; i++)
+                       {
+                               if (!ipnulls[i])
+                               {
+                                       itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
+                                       if (ItemPointerIsValid(itemptr) &&
+                                               ItemPointerGetBlockNumber(itemptr) < nblocks)
+                                               tidList[numTids++] = *itemptr;
+                               }
+                       }
+                       pfree(ipdatums);
+                       pfree(ipnulls);
+               }
+               else
+               {
+                       ItemPointerData cursor_tid;
+
+                       Assert(tidexpr->cexpr);
+                       if (execCurrentOf(tidexpr->cexpr, econtext,
+                                                         RelationGetRelid(tidstate->ss.ss_currentRelation),
+                                                         &cursor_tid))
+                       {
+                               if (numTids >= numAllocTids)
+                               {
+                                       numAllocTids *= 2;
+                                       tidList = (ItemPointerData *)
+                                               repalloc(tidList,
+                                                                numAllocTids * sizeof(ItemPointerData));
+                               }
+                               tidList[numTids++] = cursor_tid;
+                       }
+               }
+       }
+
+       /*
+        * Sort the array of TIDs into order, and eliminate duplicates.
+        * Eliminating duplicates is necessary since we want OR semantics across
+        * the list.  Sorting makes it easier to detect duplicates, and as a bonus
+        * ensures that we will visit the heap in the most efficient way.
+        */
+       if (numTids > 1)
+       {
+               int                     lastTid;
+               int                     i;
+
+               /* CurrentOfExpr could never appear OR'd with something else */
+               Assert(!tidstate->tss_isCurrentOf);
+
+               qsort((void *) tidList, numTids, sizeof(ItemPointerData),
+                         itemptr_comparator);
+               lastTid = 0;
+               for (i = 1; i < numTids; i++)
+               {
+                       if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
+                               tidList[++lastTid] = tidList[i];
+               }
+               numTids = lastTid + 1;
+       }
+
+       tidstate->tss_TidList = tidList;
+       tidstate->tss_NumTids = numTids;
+       tidstate->tss_TidPtr = -1;
+}
+
+/*
+ * qsort comparator for ItemPointerData items
+ */
+static int
+itemptr_comparator(const void *a, const void *b)
+{
+       const ItemPointerData *ipa = (const ItemPointerData *) a;
+       const ItemPointerData *ipb = (const ItemPointerData *) b;
+       BlockNumber ba = ItemPointerGetBlockNumber(ipa);
+       BlockNumber bb = ItemPointerGetBlockNumber(ipb);
+       OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
+       OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
+
+       if (ba < bb)
+               return -1;
+       if (ba > bb)
+               return 1;
+       if (oa < ob)
+               return -1;
+       if (oa > ob)
+               return 1;
+       return 0;
 }
 
 /* ----------------------------------------------------------------
@@ -64,167 +301,138 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-TidNext(TidScan *node)
+TidNext(TidScanState *node)
 {
-       EState          *estate;
-       CommonScanState *scanstate;
-       TidScanState    *tidstate;
-       ScanDirection   direction;
+       EState     *estate;
+       ScanDirection direction;
        Snapshot        snapshot;
        Relation        heapRelation;
        HeapTuple       tuple;
-       TupleTableSlot  *slot;
+       TupleTableSlot *slot;
        Buffer          buffer = InvalidBuffer;
-       int             numTids;
-
+       ItemPointerData *tidList;
+       int                     numTids;
        bool            bBackward;
-       int             tidNumber;
-       ItemPointer     *tidList, itemptr;
 
-       /* ----------------
-        *      extract necessary information from tid scan node
-        * ----------------
+       /*
+        * extract necessary information from tid scan node
         */
-       estate = node->scan.plan.state;
+       estate = node->ss.ps.state;
        direction = estate->es_direction;
        snapshot = estate->es_snapshot;
-       scanstate = node->scan.scanstate;
-       tidstate = node->tidstate;
-       heapRelation = scanstate->css_currentRelation;
-       numTids = tidstate->tss_NumTids;
-       tidList = tidstate->tss_TidList;
-       slot = scanstate->css_ScanTupleSlot;
+       heapRelation = node->ss.ss_currentRelation;
+       slot = node->ss.ss_ScanTupleSlot;
 
        /*
-        * Check if we are evaluating PlanQual for tuple of this relation.
-        * Additional checking is not good, but no other way for now. We could
-        * introduce new nodes for this case and handle TidScan --> NewNode
-        * switching in Init/ReScan plan...
+        * First time through, compute the list of TIDs to be visited
         */
-       if (estate->es_evTuple != NULL &&
-               estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
-       {
-               int     iptr, numQuals;
-
-               ExecClearTuple(slot);
-               if (estate->es_evTupleNull[node->scan.scanrelid - 1])
-                       return slot;            /* return empty slot */
-               
-               slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
-               slot->ttc_shouldFree = false;
-               /* Flag for the next call that no more tuples */
-               estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
-               return (slot);
-       }
+       if (node->tss_TidList == NULL)
+               TidListEval(node);
+
+       tidList = node->tss_TidList;
+       numTids = node->tss_NumTids;
 
-       tuple = &(tidstate->tss_htup);
+       /*
+        * We use node->tss_htup as the tuple pointer; note this can't just be a
+        * local variable here, as the scan tuple slot will keep a pointer to it.
+        */
+       tuple = &(node->tss_htup);
 
-       /* ----------------
-        *      ok, now that we have what we need, fetch an tid tuple.
-        *      if scanning this tid succeeded then return the
-        *      appropriate heap tuple.. else return NULL.
-        * ----------------
+       /*
+        * Initialize or advance scan position, depending on direction.
         */
        bBackward = ScanDirectionIsBackward(direction);
        if (bBackward)
        {
-               tidNumber = numTids - tidstate->tss_TidPtr - 1;
-               if (tidNumber < 0)
+               if (node->tss_TidPtr < 0)
                {
-                       tidNumber = 0;
-                       tidstate->tss_TidPtr = numTids - 1;
+                       /* initialize for backward scan */
+                       node->tss_TidPtr = numTids - 1;
                }
+               else
+                       node->tss_TidPtr--;
        }
        else
        {
-               if ((tidNumber = tidstate->tss_TidPtr) < 0)
+               if (node->tss_TidPtr < 0)
                {
-                       tidNumber = 0;
-                       tidstate->tss_TidPtr = 0;
+                       /* initialize for forward scan */
+                       node->tss_TidPtr = 0;
                }
+               else
+                       node->tss_TidPtr++;
        }
-       while (tidNumber < numTids)
+
+       while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
        {
-               bool            slot_is_valid = false;
+               tuple->t_self = tidList[node->tss_TidPtr];
 
-               itemptr = tidList[tidstate->tss_TidPtr];
-               tuple->t_datamcxt = NULL;
-               tuple->t_data = NULL;
-               if (itemptr)
-               {
-                       tuple->t_self = *(itemptr);
-                       heap_fetch(heapRelation, snapshot, tuple, &buffer);
-               }
-               if (tuple->t_data != NULL)
+               /*
+                * For WHERE CURRENT OF, the tuple retrieved from the cursor might
+                * since have been updated; if so, we should fetch the version that is
+                * current according to our snapshot.
+                */
+               if (node->tss_isCurrentOf)
+                       heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+
+               if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
                {
-                       bool            prev_matches = false;
-                       int             prev_tid;
-
-                       /* ----------------
-                        *      store the scanned tuple in the scan tuple slot of
-                        *      the scan state.  Eventually we will only do this and not
-                        *      return a tuple.  Note: we pass 'false' because tuples
-                        *      returned by amgetnext are pointers onto disk pages and
-                        *      were not created with palloc() and so should not be pfree()'d.
-                        * ----------------
+                       /*
+                        * Store the scanned tuple in the scan tuple slot of the scan
+                        * state.  Eventually we will only do this and not return a tuple.
                         */
-                       ExecStoreTuple(tuple,   /* tuple to store */
-                                                  slot,        /* slot to store in */
-                                                  buffer,      /* buffer associated with tuple  */
-                                                  false);      /* don't pfree */
+                       ExecStoreBufferHeapTuple(tuple, /* tuple to store */
+                                                                        slot,  /* slot to store in */
+                                                                        buffer);       /* buffer associated with
+                                                                                                * tuple */
 
                        /*
-                        * At this point we have an extra pin on the buffer,
-                        * because ExecStoreTuple incremented the pin count.
-                        * Drop our local pin.
-                       */
-                       ReleaseBuffer(buffer);  
-                       /*
-                        * We must check to see if the current tuple would have
-                        * been matched by an earlier tid, so we don't double
-                        * report it. We do this by passing the tuple through
-                        * ExecQual and look for failure with all previous
-                        * qualifications.
+                        * At this point we have an extra pin on the buffer, because
+                        * ExecStoreHeapTuple incremented the pin count. Drop our local
+                        * pin.
                         */
-                       for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr;
-                                prev_tid++)
-                       {
-                               if (ItemPointerEquals(tidList[prev_tid], &tuple->t_self))
-                               {
-                                       prev_matches = true;
-                                       break;
-                               }
-                       }
-                       if (!prev_matches)
-                               slot_is_valid = true;
-                       else
-                               ExecClearTuple(slot);
-               }
-               else if (BufferIsValid(buffer))
                        ReleaseBuffer(buffer);
-               tidNumber++;
+
+                       return slot;
+               }
+               /* Bad TID or failed snapshot qual; try next */
                if (bBackward)
-                       tidstate->tss_TidPtr--;
+                       node->tss_TidPtr--;
                else
-                       tidstate->tss_TidPtr++;
-               if (slot_is_valid)
-                       return slot; 
+                       node->tss_TidPtr++;
+
+               CHECK_FOR_INTERRUPTS();
        }
-       /* ----------------
-        *      if we get here it means the tid scan failed so we
-        *      are at the end of the scan..
-        * ----------------
+
+       /*
+        * if we get here it means the tid scan failed so we are at the end of the
+        * scan..
         */
        return ExecClearTuple(slot);
 }
 
+/*
+ * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
+ */
+static bool
+TidRecheck(TidScanState *node, TupleTableSlot *slot)
+{
+       /*
+        * XXX shouldn't we check here to make sure tuple matches TID list? In
+        * runtime-key case this is not certain, is it?  However, in the WHERE
+        * CURRENT OF case it might not match anyway ...
+        */
+       return true;
+}
+
+
 /* ----------------------------------------------------------------
  *             ExecTidScan(node)
  *
  *             Scans the relation using tids and returns
  *                the next qualifying tuple in the direction specified.
- *             It calls ExecScan() and passes it the access methods which returns
- *             the next tuple using the tids.
+ *             We call the ExecScan() routine and pass it the appropriate
+ *             access method functions.
  *
  *             Conditions:
  *               -- the "cursor" maintained by the AMI is positioned at the tuple
@@ -233,67 +441,33 @@ TidNext(TidScan *node)
  *             Initial States:
  *               -- the relation indicated is opened for scanning so that the
  *                      "cursor" is positioned before the first qualifying tuple.
- *               -- tidPtr points to the first tid.
- *               -- state variable ruleFlag = nil.
+ *               -- tidPtr is -1.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecTidScan(TidScan *node)
+static TupleTableSlot *
+ExecTidScan(PlanState *pstate)
 {
-       /* ----------------
-        *      use TidNext as access method
-        * ----------------
-        */
-       return ExecScan(&node->scan, TidNext);
+       TidScanState *node = castNode(TidScanState, pstate);
+
+       return ExecScan(&node->ss,
+                                       (ExecScanAccessMtd) TidNext,
+                                       (ExecScanRecheckMtd) TidRecheck);
 }
 
 /* ----------------------------------------------------------------
- *             ExecTidReScan(node)
+ *             ExecReScanTidScan(node)
  * ----------------------------------------------------------------
  */
 void
-ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanTidScan(TidScanState *node)
 {
-       EState          *estate;
-       TidScanState    *tidstate;
-       Plan            *outerPlan;
-       ItemPointer     *tidList;
-
-       tidstate = node->tidstate;
-       estate = node->scan.plan.state;
-       tidstate->tss_TidPtr = -1;
-       tidList = tidstate->tss_TidList;
+       if (node->tss_TidList)
+               pfree(node->tss_TidList);
+       node->tss_TidList = NULL;
+       node->tss_NumTids = 0;
+       node->tss_TidPtr = -1;
 
-       if ((outerPlan = outerPlan((Plan *) node)) != NULL)
-       {
-               /* we are scanning a subplan */
-               outerPlan = outerPlan((Plan *) node);
-               ExecReScan(outerPlan, exprCtxt, parent);
-       }
-       else
-       /* otherwise, we are scanning a relation */
-       {
-               /* If this is re-scanning of PlanQual ... */
-               if (estate->es_evTuple != NULL &&
-                       estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
-               {
-                       estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
-                       return;
-               }
-
-               /* it's possible in subselects */
-               if (exprCtxt == NULL)
-                       exprCtxt = node->scan.scanstate->cstate.cs_ExprContext;
-
-               node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
-               tidstate->tss_NumTids = TidListCreate(node->tideval, exprCtxt, tidList);
-       }
-
-       /* ----------------
-        *      perhaps return something meaningful
-        * ----------------
-        */
-       return;
+       ExecScanReScan(&node->ss);
 }
 
 /* ----------------------------------------------------------------
@@ -304,79 +478,19 @@ ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecEndTidScan(TidScan *node)
+ExecEndTidScan(TidScanState *node)
 {
-       CommonScanState *scanstate;
-       TidScanState    *tidstate;
-
-       scanstate = node->scan.scanstate;
-       tidstate = node->tidstate;
-       if (tidstate && tidstate->tss_TidList)
-               pfree(tidstate->tss_TidList);
-
-       /* ----------------
-        *      extract information from the node
-        * ----------------
-        */
-
-       /* ----------------
-        *      Free the projection info and the scan attribute info
-        *
-        *      Note: we don't ExecFreeResultType(scanstate)
-        *                because the rule manager depends on the tupType
-        *                returned by ExecMain().  So for now, this
-        *                is freed at end-transaction time.  -cim 6/2/91
-        * ----------------
-        */
-       ExecFreeProjectionInfo(&scanstate->cstate);
-
-       /* ----------------
-        *      close the heap and tid relations
-        * ----------------
+       /*
+        * Free the exprcontext
         */
-       ExecCloseR((Plan *) node);
+       ExecFreeExprContext(&node->ss.ps);
 
-       /* ----------------
-        *      clear out tuple table slots
-        * ----------------
+       /*
+        * clear out tuple table slots
         */
-       ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
-       ExecClearTuple(scanstate->css_ScanTupleSlot);
-/*       ExecClearTuple(scanstate->css_RawTupleSlot); */
-}
-
-/* ----------------------------------------------------------------
- *             ExecTidMarkPos
- *
- *             Marks scan position by marking the current tid.
- *             Returns nothing.
- * ----------------------------------------------------------------
- */
-void
-ExecTidMarkPos(TidScan *node)
-{
-       TidScanState *tidstate;
-
-       tidstate = node->tidstate;
-       tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
-}
-
-/* ----------------------------------------------------------------
- *             ExecTidRestrPos
- *
- *             Restores scan position by restoring the current tid.
- *             Returns nothing.
- *
- *             XXX Assumes previously marked scan position belongs to current tid
- * ----------------------------------------------------------------
- */
-void
-ExecTidRestrPos(TidScan *node)
-{
-       TidScanState *tidstate;
-
-       tidstate = node->tidstate;
-       tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
+       if (node->ss.ps.ps_ResultTupleSlot)
+               ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
 }
 
 /* ----------------------------------------------------------------
@@ -390,162 +504,65 @@ ExecTidRestrPos(TidScan *node)
  *               estate: the execution state initialized in InitPlan.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
+TidScanState *
+ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 {
-       TidScanState    *tidstate;
-       CommonScanState *scanstate;
-       ItemPointer     *tidList;
-       int             numTids;
-       int             tidPtr;
-       List            *rangeTable;
-       RangeTblEntry   *rtentry;
-       Oid             relid;
-       Oid             reloid;
-
+       TidScanState *tidstate;
        Relation        currentRelation;
-       int             baseid;
 
-       List       *execParam = NULL;
-
-       /* ----------------
-        *      assign execution state to node
-        * ----------------
+       /*
+        * create state structure
         */
-       node->scan.plan.state = estate;
+       tidstate = makeNode(TidScanState);
+       tidstate->ss.ps.plan = (Plan *) node;
+       tidstate->ss.ps.state = estate;
+       tidstate->ss.ps.ExecProcNode = ExecTidScan;
 
-       /* --------------------------------
-        *      Part 1)  initialize scan state
+       /*
+        * Miscellaneous initialization
         *
-        *      create new CommonScanState for node
-        * --------------------------------
+        * create expression context for node
         */
-       scanstate = makeNode(CommonScanState);
-/*
-       scanstate->ss_ProcOuterFlag = false;
-       scanstate->ss_OldRelId = 0;
-*/
-
-       node->scan.scanstate = scanstate;
+       ExecAssignExprContext(estate, &tidstate->ss.ps);
 
-       /* ----------------
-        *      assign node's base_id .. we don't use AssignNodeBaseid() because
-        *      the increment is done later on after we assign the tid scan's
-        *      scanstate.      see below.
-        * ----------------
-        */
-       baseid = estate->es_BaseId;
-/*       scanstate->csstate.cstate.bnode.base_id = baseid; */
-       scanstate->cstate.cs_base_id = baseid;
-
-       /* ----------------
-        *      create expression context for node
-        * ----------------
-        */
-       ExecAssignExprContext(estate, &scanstate->cstate);
-
-#define TIDSCAN_NSLOTS 3
-       /* ----------------
-        *      tuple table initialization
-        * ----------------
-        */
-       ExecInitResultTupleSlot(estate, &scanstate->cstate);
-       ExecInitScanTupleSlot(estate, scanstate);
-/*       ExecInitRawTupleSlot(estate, scanstate); */
-
-       /* ----------------
-        *      initialize projection info.  result type comes from scan desc
-        *      below..
-        * ----------------
-        */
-       ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
-       /* --------------------------------
-         *  Part 2)  initialize tid scan state
-         *
-         *  create new TidScanState for node
-         * --------------------------------
-         */
-       tidstate = makeNode(TidScanState);
-       node->tidstate = tidstate;
-
-       /* ----------------
-        *      assign base id to tid scan state also
-        * ----------------
+       /*
+        * mark tid list as not computed yet
         */
-       tidstate->cstate.cs_base_id = baseid;
-       baseid++;
-       estate->es_BaseId = baseid;
+       tidstate->tss_TidList = NULL;
+       tidstate->tss_NumTids = 0;
+       tidstate->tss_TidPtr = -1;
 
-       /* ----------------
-        *      get the tid node information
-        * ----------------
+       /*
+        * open the scan relation
         */
-       tidList = (ItemPointer *)palloc(length(node->tideval) * sizeof(ItemPointer));
-       numTids = 0;
-       if (!node->needRescan)
-               numTids = TidListCreate(node->tideval, scanstate->cstate.cs_ExprContext, tidList);
-       tidPtr = -1;
+       currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
 
-       CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
+       tidstate->ss.ss_currentRelation = currentRelation;
+       tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
 
-       tidstate->tss_NumTids = numTids;
-       tidstate->tss_TidPtr = tidPtr;
-       tidstate->tss_TidList = tidList;
-
-       /* ----------------
-        *      get the range table and direction information
-        *      from the execution state (these are needed to
-        *      open the relations).
-        * ----------------
+       /*
+        * get the scan type from the relation descriptor.
         */
-       rangeTable = estate->es_range_table;
+       ExecInitScanTupleSlot(estate, &tidstate->ss,
+                                                 RelationGetDescr(currentRelation),
+                                                 &TTSOpsBufferHeapTuple);
 
-       /* ----------------
-        *      open the base relation
-        * ----------------
-        */
-       relid = node->scan.scanrelid;
-       rtentry = rt_fetch(relid, rangeTable);
-       reloid = rtentry->relid;
-
-       currentRelation = heap_open(reloid, AccessShareLock);
-        if (currentRelation == NULL)
-                elog(ERROR, "ExecInitTidScan heap_open failed."); 
-       scanstate->css_currentRelation = currentRelation;
-       scanstate->css_currentScanDesc = 0;
-
-       /* ----------------
-        *      get the scan type from the relation descriptor.
-        * ----------------
+       /*
+        * Initialize result type and projection.
         */
-       ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
-       ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+       ExecInitResultTypeTL(&tidstate->ss.ps);
+       ExecAssignScanProjectionInfo(&tidstate->ss);
 
-       /* ----------------
-        *      tid scans don't have subtrees..
-        * ----------------
+       /*
+        * initialize child expressions
         */
-/*       scanstate->ss_ProcOuterFlag = false; */
+       tidstate->ss.ps.qual =
+               ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
 
-       tidstate->cstate.cs_TupFromTlist = false;
+       TidExprListCreate(tidstate);
 
        /*
-        * if there are some PARAM_EXEC in skankeys then force tid rescan on
-        * first scan.
-        */
-       ((Plan *) node)->chgParam = execParam;
-
-       /* ----------------
-        *      all done.
-        * ----------------
+        * all done.
         */
-       return TRUE;
-}
-
-int
-ExecCountSlotsTidScan(TidScan *node)
-{
-       return ExecCountSlotsNode(outerPlan((Plan *) node)) +
-       ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
+       return tidstate;
 }