diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 67 | ||||
-rw-r--r-- | src/backend/optimizer/path/tidpath.c | 111 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 20 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 17 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 16 |
6 files changed, 169 insertions, 66 deletions
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; } |