aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c25
-rw-r--r--src/backend/executor/nodeTidscan.c223
-rw-r--r--src/backend/nodes/copyfuncs.c4
-rw-r--r--src/backend/nodes/outfuncs.c6
-rw-r--r--src/backend/optimizer/path/costsize.c67
-rw-r--r--src/backend/optimizer/path/tidpath.c111
-rw-r--r--src/backend/optimizer/plan/createplan.c20
-rw-r--r--src/backend/optimizer/plan/setrefs.c17
-rw-r--r--src/backend/optimizer/plan/subselect.c4
-rw-r--r--src/backend/optimizer/util/pathnode.c16
-rw-r--r--src/include/nodes/execnodes.h8
-rw-r--r--src/include/nodes/plannodes.h7
-rw-r--r--src/include/nodes/relation.h9
-rw-r--r--src/include/optimizer/cost.h4
-rw-r--r--src/include/optimizer/pathnode.h4
15 files changed, 378 insertions, 147 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e8cdfbad882..f60fff36c4c 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 4b0775719e5..ba4b7f3bca8 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
@@ -412,18 +521,12 @@ ExecInitTidScan(TidScan *node, EState *estate)
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 ba1a476ad0f..7d708e3fb1d 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 16acd5d7214..5ba31580ae7 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 1d5e66337ce..e45e454a375 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;
@@ -644,6 +666,34 @@ cost_tidscan(Path *path, PlannerInfo *root,
}
/*
+ * 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 26058dc1b64..91d30805012 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 3bd760fda3a..4acac8421c8 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 9a8d83e8a7b..5a716ead476 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 115e462cf0c..5775b0521fb 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 934daf8b28f..624cf506a35 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 82f182c3497..e9fb41f653a 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 01333707d41..82b233b9b5f 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 15d5647282c..aa6217d0313 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 eeec6b1f1bf..16b256d9fda 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 473043cbad1..6c8d62ac44f 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);