diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e8cdfbad88..f60fff36c4 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 4b0775719e..ba4b7f3bca 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 ba1a476ad0..7d708e3fb1 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 16acd5d721..5ba31580ae 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 1d5e66337c..e45e454a37 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 26058dc1b6..91d3080501 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 other; /* success */ + 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; + + 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 3bd760fda3..4acac8421c 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 9a8d83e8a7..5a716ead47 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 115e462cf0..5775b0521f 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 934daf8b28..624cf506a3 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; + pathnode->tidquals = tidquals; - cost_tidscan(&pathnode->path, root, rel, tideval); - - /* - * 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 82f182c349..e9fb41f653 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 01333707d4..82b233b9b5 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 15d5647282..aa6217d031 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 eeec6b1f1b..16b256d9fd 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 473043cbad..6c8d62ac44 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);