diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e8cdfbad882ac55e3b425e2efcb7488a11ccf012..f60fff36c4c7534a167dcbeefab543adf04720c1 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.140 2005/11/22 18:17:09 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.141 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -724,7 +724,6 @@ explain_outNode(StringInfo str,
 						   str, indent, es);
 			/* FALL THRU */
 		case T_SeqScan:
-		case T_TidScan:
 		case T_SubqueryScan:
 		case T_FunctionScan:
 			show_scan_qual(plan->qual,
@@ -733,6 +732,28 @@ explain_outNode(StringInfo str,
 						   outer_plan,
 						   str, indent, es);
 			break;
+		case T_TidScan:
+			{
+				/*
+				 * The tidquals list has OR semantics, so be sure to show it
+				 * as an OR condition.
+				 */
+				List *tidquals = ((TidScan *) plan)->tidquals;
+
+				if (list_length(tidquals) > 1)
+					tidquals = list_make1(make_orclause(tidquals));
+				show_scan_qual(tidquals,
+							   "TID Cond",
+							   ((Scan *) plan)->scanrelid,
+							   outer_plan,
+							   str, indent, es);
+				show_scan_qual(plan->qual,
+							   "Filter",
+							   ((Scan *) plan)->scanrelid,
+							   outer_plan,
+							   str, indent, es);
+			}
+			break;
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
 							"Join Filter",
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 4b0775719e5c0b324fa7aaaf7fdf2d885ee239a7..ba4b7f3bca8f77a3f963f127227a3a73a676f142 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.44 2005/11/25 04:24:48 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.45 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,47 +24,156 @@
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
+#include "catalog/pg_type.h"
 #include "executor/execdebug.h"
 #include "executor/nodeTidscan.h"
-#include "access/heapam.h"
+#include "optimizer/clauses.h"
 #include "parser/parsetree.h"
+#include "utils/array.h"
 
 
+#define IsCTIDVar(node)  \
+	((node) != NULL && \
+	 IsA((node), Var) && \
+	 ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
+	 ((Var *) (node))->varlevelsup == 0)
+
 static void TidListCreate(TidScanState *tidstate);
+static int	itemptr_comparator(const void *a, const void *b);
 static TupleTableSlot *TidNext(TidScanState *node);
 
 
 /*
  * 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
 TidListCreate(TidScanState *tidstate)
 {
-	List	   *evalList = tidstate->tss_tideval;
+	List	   *evalList = tidstate->tss_tidquals;
 	ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
 	ItemPointerData *tidList;
-	int			numTids = 0;
+	int			numAllocTids;
+	int			numTids;
 	ListCell   *l;
 
+	/*
+	 * We initialize the array with enough slots for the case that all
+	 * quals are simple OpExprs.  If there's any ScalarArrayOpExprs,
+	 * we may have to enlarge the array.
+	 */
+	numAllocTids = list_length(evalList);
 	tidList = (ItemPointerData *)
-		palloc(list_length(tidstate->tss_tideval) * sizeof(ItemPointerData));
+		palloc(numAllocTids * sizeof(ItemPointerData));
+	numTids = 0;
 
 	foreach(l, evalList)
 	{
+		ExprState  *exstate = (ExprState *) lfirst(l);
+		Expr	   *expr = exstate->expr;
 		ItemPointer itemptr;
 		bool		isNull;
 
-		itemptr = (ItemPointer)
-			DatumGetPointer(ExecEvalExprSwitchContext(lfirst(l),
-													  econtext,
-													  &isNull,
-													  NULL));
-		if (!isNull && itemptr && ItemPointerIsValid(itemptr))
+		if (is_opclause(expr))
 		{
-			tidList[numTids] = *itemptr;
-			numTids++;
+			FuncExprState  *fexstate = (FuncExprState *) exstate;
+			Node *arg1;
+			Node *arg2;
+
+			arg1 = get_leftop(expr);
+			arg2 = get_rightop(expr);
+			if (IsCTIDVar(arg1))
+				exstate = (ExprState *) lsecond(fexstate->args);
+			else if (IsCTIDVar(arg2))
+				exstate = (ExprState *) linitial(fexstate->args);
+			else
+				elog(ERROR, "could not identify CTID variable");
+
+			itemptr = (ItemPointer)
+				DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+														  econtext,
+														  &isNull,
+														  NULL));
+			if (!isNull && ItemPointerIsValid(itemptr))
+			{
+				if (numTids >= numAllocTids)
+				{
+					numAllocTids *= 2;
+					tidList = (ItemPointerData *)
+						repalloc(tidList,
+								 numAllocTids * sizeof(ItemPointerData));
+				}
+				tidList[numTids++] = *itemptr;
+			}
 		}
+		else if (expr && IsA(expr, ScalarArrayOpExpr))
+		{
+			ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
+			Datum		arraydatum;
+			ArrayType  *itemarray;
+			Datum	   *ipdatums;
+			bool	   *ipnulls;
+			int			ndatums;
+			int			i;
+
+			exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
+			arraydatum = ExecEvalExprSwitchContext(exstate,
+												   econtext,
+												   &isNull,
+												   NULL);
+			if (isNull)
+				continue;
+			itemarray = DatumGetArrayTypeP(arraydatum);
+			deconstruct_array(itemarray,
+							  TIDOID, SizeOfIptrData, 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))
+						tidList[numTids++] = *itemptr;
+				}
+			}
+			pfree(ipdatums);
+			pfree(ipnulls);
+		}
+		else
+			elog(ERROR, "could not identify CTID expression");
+	}
+
+	/*
+	 * 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;
+
+		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;
@@ -72,6 +181,30 @@ TidListCreate(TidScanState *tidstate)
 	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;
+}
+
 /* ----------------------------------------------------------------
  *		TidNext
  *
@@ -94,7 +227,6 @@ TidNext(TidScanState *node)
 	ItemPointerData *tidList;
 	int			numTids;
 	bool		bBackward;
-	int			tidNumber;
 
 	/*
 	 * extract necessary information from tid scan node
@@ -143,38 +275,35 @@ TidNext(TidScanState *node)
 	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 - node->tss_TidPtr - 1;
-		if (tidNumber < 0)
+		if (node->tss_TidPtr < 0)
 		{
-			tidNumber = 0;
+			/* initialize for backward scan */
 			node->tss_TidPtr = numTids - 1;
 		}
+		else
+			node->tss_TidPtr--;
 	}
 	else
 	{
-		if ((tidNumber = node->tss_TidPtr) < 0)
+		if (node->tss_TidPtr < 0)
 		{
-			tidNumber = 0;
+			/* initialize for forward scan */
 			node->tss_TidPtr = 0;
 		}
+		else
+			node->tss_TidPtr++;
 	}
-	while (tidNumber < numTids)
-	{
-		bool		slot_is_valid = false;
 
+	while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
+	{
 		tuple->t_self = tidList[node->tss_TidPtr];
 		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.
@@ -193,31 +322,13 @@ TidNext(TidScanState *node)
 			 */
 			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.
-			 */
-			for (prev_tid = 0; prev_tid < node->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);
+			return slot;
 		}
-		tidNumber++;
+		/* Bad TID or failed snapshot qual; try next */
 		if (bBackward)
 			node->tss_TidPtr--;
 		else
 			node->tss_TidPtr++;
-		if (slot_is_valid)
-			return slot;
 	}
 
 	/*
@@ -242,8 +353,7 @@ TidNext(TidScanState *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 *
@@ -362,7 +472,6 @@ TidScanState *
 ExecInitTidScan(TidScan *node, EState *estate)
 {
 	TidScanState *tidstate;
-	List	   *rangeTable;
 	RangeTblEntry *rtentry;
 	Oid			relid;
 	Oid			reloid;
@@ -392,8 +501,8 @@ ExecInitTidScan(TidScan *node, EState *estate)
 		ExecInitExpr((Expr *) node->scan.plan.qual,
 					 (PlanState *) tidstate);
 
-	tidstate->tss_tideval = (List *)
-		ExecInitExpr((Expr *) node->tideval,
+	tidstate->tss_tidquals = (List *)
+		ExecInitExpr((Expr *) node->tidquals,
 					 (PlanState *) tidstate);
 
 #define TIDSCAN_NSLOTS 2
@@ -411,19 +520,13 @@ ExecInitTidScan(TidScan *node, EState *estate)
 	tidstate->tss_NumTids = 0;
 	tidstate->tss_TidPtr = -1;
 
-	/*
-	 * get the range table and direction information from the execution state
-	 * (these are needed to open the relations).
-	 */
-	rangeTable = estate->es_range_table;
-
 	/*
 	 * open the base relation
 	 *
 	 * We acquire AccessShareLock for the duration of the scan.
 	 */
 	relid = node->scan.scanrelid;
-	rtentry = rt_fetch(relid, rangeTable);
+	rtentry = rt_fetch(relid, estate->es_range_table);
 	reloid = rtentry->relid;
 
 	currentRelation = heap_open(reloid, AccessShareLock);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ba1a476ad0fe09e2178ab17492a5361af99629de..7d708e3fb1dc16b2cb3f56d5a1e43633d1cb4f4c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.321 2005/11/22 18:17:11 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.322 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -325,7 +325,7 @@ _copyTidScan(TidScan *from)
 	/*
 	 * copy remainder of node
 	 */
-	COPY_NODE_FIELD(tideval);
+	COPY_NODE_FIELD(tidquals);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 16acd5d72140d48e5bde33f0dccec58938e943d1..5ba31580ae7bc0bdbb4e69eea6fec257efd1c1d6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.262 2005/11/14 23:54:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.263 2005/11/26 22:14:56 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -390,7 +390,7 @@ _outTidScan(StringInfo str, TidScan *node)
 
 	_outScanInfo(str, (Scan *) node);
 
-	WRITE_NODE_FIELD(tideval);
+	WRITE_NODE_FIELD(tidquals);
 }
 
 static void
@@ -1079,7 +1079,7 @@ _outTidPath(StringInfo str, TidPath *node)
 
 	_outPathInfo(str, (Path *) node);
 
-	WRITE_NODE_FIELD(tideval);
+	WRITE_NODE_FIELD(tidquals);
 }
 
 static void
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 1d5e66337ceb36f2425b61612a22531c70949abf..e45e454a3751c6308b134007259dc494ef1a1540 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.150 2005/11/22 18:17:12 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.151 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -66,6 +66,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/plancat.h"
 #include "parser/parsetree.h"
+#include "utils/array.h"
 #include "utils/selfuncs.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -104,6 +105,7 @@ bool		enable_hashjoin = true;
 
 
 static bool cost_qual_eval_walker(Node *node, QualCost *total);
+static int	estimate_array_length(Node *arrayexpr);
 static Selectivity approx_selectivity(PlannerInfo *root, List *quals,
 				   JoinType jointype);
 static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root);
@@ -617,12 +619,13 @@ cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root)
  */
 void
 cost_tidscan(Path *path, PlannerInfo *root,
-			 RelOptInfo *baserel, List *tideval)
+			 RelOptInfo *baserel, List *tidquals)
 {
 	Cost		startup_cost = 0;
 	Cost		run_cost = 0;
 	Cost		cpu_per_tuple;
-	int			ntuples = list_length(tideval);
+	int			ntuples;
+	ListCell   *l;
 
 	/* Should only be applied to base relations */
 	Assert(baserel->relid > 0);
@@ -631,6 +634,25 @@ cost_tidscan(Path *path, PlannerInfo *root,
 	if (!enable_tidscan)
 		startup_cost += disable_cost;
 
+	/* Count how many tuples we expect to retrieve */
+	ntuples = 0;
+	foreach(l, tidquals)
+	{
+		if (IsA(lfirst(l), ScalarArrayOpExpr))
+		{
+			/* Each element of the array yields 1 tuple */
+			ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) lfirst(l);
+			Node *arraynode = (Node *) lsecond(saop->args);
+
+			ntuples += estimate_array_length(arraynode);
+		}
+		else
+		{
+			/* It's just CTID = something, count 1 tuple */
+			ntuples++;
+		}
+	}
+
 	/* disk costs --- assume each tuple on a different page */
 	run_cost += random_page_cost * ntuples;
 
@@ -643,6 +665,34 @@ cost_tidscan(Path *path, PlannerInfo *root,
 	path->total_cost = startup_cost + run_cost;
 }
 
+/*
+ * Estimate number of elements in the array yielded by an expression.
+ */
+static int
+estimate_array_length(Node *arrayexpr)
+{
+	if (arrayexpr && IsA(arrayexpr, Const))
+	{
+		Datum		arraydatum = ((Const *) arrayexpr)->constvalue;
+		bool		arrayisnull = ((Const *) arrayexpr)->constisnull;
+		ArrayType  *arrayval;
+
+		if (arrayisnull)
+			return 0;
+		arrayval = DatumGetArrayTypeP(arraydatum);
+		return ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
+	}
+	else if (arrayexpr && IsA(arrayexpr, ArrayExpr))
+	{
+		return list_length(((ArrayExpr *) arrayexpr)->elements);
+	}
+	else
+	{
+		/* default guess */
+		return 10;
+	}
+}
+
 /*
  * cost_subqueryscan
  *	  Determines and returns the cost of scanning a subquery RTE.
@@ -1549,8 +1599,15 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 		total->per_tuple += cpu_operator_cost;
 	else if (IsA(node, ScalarArrayOpExpr))
 	{
-		/* should charge more than 1 op cost, but how many? */
-		total->per_tuple += cpu_operator_cost * 10;
+		/*
+		 * Estimate that the operator will be applied to about half of the
+		 * array elements before the answer is determined.
+		 */
+		ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
+		Node *arraynode = (Node *) lsecond(saop->args);
+
+		total->per_tuple +=
+			cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
 	}
 	else if (IsA(node, SubLink))
 	{
diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c
index 26058dc1b6455f0ccee98bf0dabc5eb7f926cddc..91d3080501216641d5302fd24bd5909afc344d53 100644
--- a/src/backend/optimizer/path/tidpath.c
+++ b/src/backend/optimizer/path/tidpath.c
@@ -6,8 +6,10 @@
  *
  * What we are looking for here is WHERE conditions of the form
  * "CTID = pseudoconstant", which can be implemented by just fetching
- * the tuple directly via heap_fetch().  We can also handle OR conditions
- * if each OR arm contains such a condition; in particular this allows
+ * the tuple directly via heap_fetch().  We can also handle OR'd conditions
+ * such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
+ * conditions of the form CTID = ANY(pseudoconstant_array).  In particular
+ * this allows
  *		WHERE ctid IN (tid1, tid2, ...)
  *
  * There is currently no special support for joins involving CTID; in
@@ -22,7 +24,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.25 2005/10/15 02:49:20 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.26 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,9 +39,10 @@
 #include "parser/parse_expr.h"
 
 
-static Node *IsTidEqualClause(int varno, OpExpr *node);
-static List *TidQualFromExpr(int varno, Node *expr);
-static List *TidQualFromRestrictinfo(int varno, List *restrictinfo);
+static bool IsTidEqualClause(OpExpr *node, int varno);
+static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
+static List *TidQualFromExpr(Node *expr, int varno);
+static List *TidQualFromRestrictinfo(List *restrictinfo, int varno);
 
 
 /*
@@ -48,14 +51,12 @@ static List *TidQualFromRestrictinfo(int varno, List *restrictinfo);
  * or
  *		pseudoconstant = CTID
  *
- * If it is, return the pseudoconstant subnode; if not, return NULL.
- *
  * We check that the CTID Var belongs to relation "varno".	That is probably
  * redundant considering this is only applied to restriction clauses, but
  * let's be safe.
  */
-static Node *
-IsTidEqualClause(int varno, OpExpr *node)
+static bool
+IsTidEqualClause(OpExpr *node, int varno)
 {
 	Node	   *arg1,
 			   *arg2,
@@ -64,9 +65,9 @@ IsTidEqualClause(int varno, OpExpr *node)
 
 	/* Operator must be tideq */
 	if (node->opno != TIDEqualOperator)
-		return NULL;
+		return false;
 	if (list_length(node->args) != 2)
-		return NULL;
+		return false;
 	arg1 = linitial(node->args);
 	arg2 = lsecond(node->args);
 
@@ -91,20 +92,61 @@ IsTidEqualClause(int varno, OpExpr *node)
 			other = arg1;
 	}
 	if (!other)
-		return NULL;
+		return false;
 	if (exprType(other) != TIDOID)
-		return NULL;			/* probably can't happen */
+		return false;			/* probably can't happen */
 
 	/* The other argument must be a pseudoconstant */
 	if (!is_pseudo_constant_clause(other))
-		return NULL;
+		return false;
+
+	return true;				/* success */
+}
+
+/*
+ * Check to see if a clause is of the form
+ *		CTID = ANY (pseudoconstant_array)
+ */
+static bool
+IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
+{
+	Node	   *arg1,
+			   *arg2;
+
+	/* Operator must be tideq */
+	if (node->opno != TIDEqualOperator)
+		return false;
+	if (!node->useOr)
+		return false;
+	Assert(list_length(node->args) == 2);
+	arg1 = linitial(node->args);
+	arg2 = lsecond(node->args);
+
+	/* CTID must be first argument */
+	if (arg1 && IsA(arg1, Var))
+	{
+		Var	   *var = (Var *) arg1;
 
-	return other;				/* success */
+		if (var->varattno == SelfItemPointerAttributeNumber &&
+			var->vartype == TIDOID &&
+			var->varno == varno &&
+			var->varlevelsup == 0)
+		{
+			/* The other argument must be a pseudoconstant */
+			if (is_pseudo_constant_clause(arg2))
+				return true;	/* success */
+		}
+	}
+
+	return false;
 }
 
 /*
  *	Extract a set of CTID conditions from the given qual expression
  *
+ *	Returns a List of CTID qual expressions (with implicit OR semantics
+ *	across the list), or NIL if there are no usable conditions.
+ *
  *	If the expression is an AND clause, we can use a CTID condition
  *	from any sub-clause.  If it is an OR clause, we must be able to
  *	extract a CTID condition from every sub-clause, or we can't use it.
@@ -113,30 +155,30 @@ IsTidEqualClause(int varno, OpExpr *node)
  *	sub-clauses, in which case we could try to pick the most efficient one.
  *	In practice, such usage seems very unlikely, so we don't bother; we
  *	just exit as soon as we find the first candidate.
- *
- *	Returns a List of pseudoconstant TID expressions, or NIL if no match.
- *	(Has to be a list for the OR case.)
  */
 static List *
-TidQualFromExpr(int varno, Node *expr)
+TidQualFromExpr(Node *expr, int varno)
 {
-	List	   *rlst = NIL,
-			   *frtn;
+	List	   *rlst = NIL;
 	ListCell   *l;
-	Node	   *rnode;
 
 	if (is_opclause(expr))
 	{
 		/* base case: check for tideq opclause */
-		rnode = IsTidEqualClause(varno, (OpExpr *) expr);
-		if (rnode)
-			rlst = list_make1(rnode);
+		if (IsTidEqualClause((OpExpr *) expr, varno))
+			rlst = list_make1(expr);
+	}
+	else if (expr && IsA(expr, ScalarArrayOpExpr))
+	{
+		/* another base case: check for tid = ANY clause */
+		if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
+			rlst = list_make1(expr);
 	}
 	else if (and_clause(expr))
 	{
 		foreach(l, ((BoolExpr *) expr)->args)
 		{
-			rlst = TidQualFromExpr(varno, (Node *) lfirst(l));
+			rlst = TidQualFromExpr((Node *) lfirst(l), varno);
 			if (rlst)
 				break;
 		}
@@ -145,7 +187,8 @@ TidQualFromExpr(int varno, Node *expr)
 	{
 		foreach(l, ((BoolExpr *) expr)->args)
 		{
-			frtn = TidQualFromExpr(varno, (Node *) lfirst(l));
+			List   *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
+
 			if (frtn)
 				rlst = list_concat(rlst, frtn);
 			else
@@ -167,7 +210,7 @@ TidQualFromExpr(int varno, Node *expr)
  *	except for the format of the input.
  */
 static List *
-TidQualFromRestrictinfo(int varno, List *restrictinfo)
+TidQualFromRestrictinfo(List *restrictinfo, int varno)
 {
 	List	   *rlst = NIL;
 	ListCell   *l;
@@ -178,7 +221,7 @@ TidQualFromRestrictinfo(int varno, List *restrictinfo)
 
 		if (!IsA(rinfo, RestrictInfo))
 			continue;			/* probably should never happen */
-		rlst = TidQualFromExpr(varno, (Node *) rinfo->clause);
+		rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
 		if (rlst)
 			break;
 	}
@@ -194,10 +237,10 @@ TidQualFromRestrictinfo(int varno, List *restrictinfo)
 void
 create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
 {
-	List	   *tideval;
+	List	   *tidquals;
 
-	tideval = TidQualFromRestrictinfo(rel->relid, rel->baserestrictinfo);
+	tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
 
-	if (tideval)
-		add_path(rel, (Path *) create_tidscan_path(root, rel, tideval));
+	if (tidquals)
+		add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals));
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3bd760fda3a6714c047ce9dd3d7f265603e5ce08..4acac8421c863ccf2c23e4ce851b4c58f97d3447 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.204 2005/11/25 19:47:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.205 2005/11/26 22:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,7 +92,7 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
 					 List *bitmapqualorig,
 					 Index scanrelid);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
-			 List *tideval);
+			 List *tidquals);
 static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
 				  Index scanrelid);
 static BitmapAnd *make_bitmap_and(List *bitmapplans);
@@ -1149,6 +1149,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
 {
 	TidScan    *scan_plan;
 	Index		scan_relid = best_path->path.parent->relid;
+	List	   *ortidquals;
 
 	/* it should be a base rel... */
 	Assert(scan_relid > 0);
@@ -1157,13 +1158,22 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
 	/* Reduce RestrictInfo list to bare expressions */
 	scan_clauses = get_actual_clauses(scan_clauses);
 
+	/*
+	 * Remove any clauses that are TID quals.  This is a bit tricky since
+	 * the tidquals list has implicit OR semantics.
+	 */
+	ortidquals = best_path->tidquals;
+	if (list_length(ortidquals) > 1)
+		ortidquals = list_make1(make_orclause(ortidquals));
+	scan_clauses = list_difference(scan_clauses, ortidquals);
+
 	/* Sort clauses into best execution order */
 	scan_clauses = order_qual_clauses(root, scan_clauses);
 
 	scan_plan = make_tidscan(tlist,
 							 scan_clauses,
 							 scan_relid,
-							 best_path->tideval);
+							 best_path->tidquals);
 
 	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
 
@@ -1939,7 +1949,7 @@ static TidScan *
 make_tidscan(List *qptlist,
 			 List *qpqual,
 			 Index scanrelid,
-			 List *tideval)
+			 List *tidquals)
 {
 	TidScan    *node = makeNode(TidScan);
 	Plan	   *plan = &node->scan.plan;
@@ -1950,7 +1960,7 @@ make_tidscan(List *qptlist,
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
-	node->tideval = tideval;
+	node->tidquals = tidquals;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 9a8d83e8a7b76a6dea14e884af2f94f29cd50bf1..5a716ead47668534c4a3fe5d45d96a12df3bb417 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.118 2005/11/22 18:17:13 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.119 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -170,8 +170,7 @@ set_plan_references(Plan *plan, List *rtable)
 		case T_TidScan:
 			fix_expr_references(plan, (Node *) plan->targetlist);
 			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan,
-								(Node *) ((TidScan *) plan)->tideval);
+			fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals);
 			break;
 		case T_SubqueryScan:
 			/* Needs special treatment, see comments below */
@@ -509,7 +508,7 @@ adjust_plan_varnos(Plan *plan, int rtoffset)
 			((TidScan *) plan)->scan.scanrelid += rtoffset;
 			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
 			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((TidScan *) plan)->tideval,
+			adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals,
 							   rtoffset);
 			break;
 		case T_SubqueryScan:
@@ -916,11 +915,11 @@ set_inner_join_references(Plan *inner_plan,
 		TidScan    *innerscan = (TidScan *) inner_plan;
 		Index		innerrel = innerscan->scan.scanrelid;
 
-		innerscan->tideval = join_references(innerscan->tideval,
-											 rtable,
-											 outer_itlist,
-											 NULL,
-											 innerrel);
+		innerscan->tidquals = join_references(innerscan->tidquals,
+											  rtable,
+											  outer_itlist,
+											  NULL,
+											  innerrel);
 	}
 }
 
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 115e462cf0c72d3ab87bd243e907de7cbcb32762..5775b0521fbd030ab9e0eea43834b2204c0d7de8 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.101 2005/11/22 18:17:13 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.102 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1024,7 +1024,7 @@ finalize_plan(Plan *plan, List *rtable,
 			break;
 
 		case T_TidScan:
-			finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+			finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
 							  &context);
 			break;
 
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 934daf8b28ff00c570bea4dec1d9ba3becabab1e..624cf506a357fa25d2334176dce5f89e8b9ea8c1 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.125 2005/10/15 02:49:21 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.126 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -613,11 +613,10 @@ create_bitmap_or_path(PlannerInfo *root,
 
 /*
  * create_tidscan_path
- *	  Creates a path corresponding to a tid_direct scan, returning the
- *	  pathnode.
+ *	  Creates a path corresponding to a scan by TID, returning the pathnode.
  */
 TidPath *
-create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tideval)
+create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals)
 {
 	TidPath    *pathnode = makeNode(TidPath);
 
@@ -625,14 +624,9 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tideval)
 	pathnode->path.parent = rel;
 	pathnode->path.pathkeys = NIL;
 
-	pathnode->tideval = tideval;
-
-	cost_tidscan(&pathnode->path, root, rel, tideval);
+	pathnode->tidquals = tidquals;
 
-	/*
-	 * divide selectivity for each clause to get an equal selectivity as
-	 * IndexScan does OK ?
-	 */
+	cost_tidscan(&pathnode->path, root, rel, tidquals);
 
 	return pathnode;
 }
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 82f182c3497f06dad4cc6514ae926613b4eaa086..e9fb41f653a06207588eebf11aa5d4d6c947d29b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.143 2005/11/26 03:03:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.144 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -972,14 +972,14 @@ typedef struct BitmapHeapScanState
  *	 TidScanState information
  *
  *		NumTids		   number of tids in this scan
- *		TidPtr		   current tid in use
- *		TidList		   evaluated item pointers
+ *		TidPtr		   index of currently fetched tid
+ *		TidList		   evaluated item pointers (array of size NumTids)
  * ----------------
  */
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tideval;	/* list of ExprState nodes */
+	List	   *tss_tidquals;	/* list of ExprState nodes */
 	int			tss_NumTids;
 	int			tss_TidPtr;
 	int			tss_MarkTidPtr;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 01333707d41709ae7aa25bf81873c006ac1ba6ad..82b233b9b5f4779cf9b6e011a7a410a61d12b32e 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.81 2005/11/22 18:17:31 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.82 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -255,12 +255,15 @@ typedef struct BitmapHeapScan
 
 /* ----------------
  *		tid scan node
+ *
+ * tidquals is an implicitly OR'ed list of qual expressions of the form
+ * "CTID = pseudoconstant" or "CTID = ANY(pseudoconstant_array)".
  * ----------------
  */
 typedef struct TidScan
 {
 	Scan		scan;
-	List	   *tideval;
+	List	   *tidquals;		/* qual(s) involving CTID = something */
 } TidScan;
 
 /* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 15d5647282cb563d626fd93b2977480e1021f887..aa6217d0313a31f269e6a4a8706eb174ba7ed353 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.120 2005/11/14 23:54:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.121 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -503,13 +503,14 @@ typedef struct BitmapOrPath
 /*
  * TidPath represents a scan by TID
  *
- * tideval is an implicitly OR'ed list of quals of the form CTID = something.
- * Note they are bare quals, not RestrictInfos.
+ * tidquals is an implicitly OR'ed list of qual expressions of the form
+ * "CTID = pseudoconstant" or "CTID = ANY(pseudoconstant_array)".
+ * Note they are bare expressions, not RestrictInfos.
  */
 typedef struct TidPath
 {
 	Path		path;
-	List	   *tideval;		/* qual(s) involving CTID = something */
+	List	   *tidquals;		/* qual(s) involving CTID = something */
 } TidPath;
 
 /*
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index eeec6b1f1bf773b0eee368e045cb1b7180028c2e..16b256d9fda14f7d25475d2e7d51fd84152d891e 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.71 2005/10/15 02:49:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.72 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,7 +61,7 @@ extern void cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root);
 extern void cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root);
 extern void cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec);
 extern void cost_tidscan(Path *path, PlannerInfo *root,
-			 RelOptInfo *baserel, List *tideval);
+			 RelOptInfo *baserel, List *tidquals);
 extern void cost_subqueryscan(Path *path, RelOptInfo *baserel);
 extern void cost_functionscan(Path *path, PlannerInfo *root,
 				  RelOptInfo *baserel);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 473043cbad1e7b3263c447c1eabc066d86f4c547..6c8d62ac44fcc79b1237a7a7bb1ab96a1fb6d814 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.62 2005/10/15 02:49:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.63 2005/11/26 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
 					  RelOptInfo *rel,
 					  List *bitmapquals);
 extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
-					List *tideval);
+					List *tidquals);
 extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
 extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath,
 				   List *constantqual);