diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index b35acb7..a8354e4 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -4012,21 +4012,70 @@ void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel) { double nrows; + Selectivity pre_s, s; + List *clause_list; + ListCell *l; + QualCost cost; /* Should only be applied to base relations */ Assert(rel->relid > 0); - nrows = rel->tuples * - clauselist_selectivity(root, - rel->baserestrictinfo, - 0, - JOIN_INNER, - NULL); - - rel->rows = clamp_row_est(nrows); + /* + * When 'clause_list' consists of multiple quals, each qual will be + * evaluated different times because earlier quals filter some input + * tuples. At this point, the order of quals in 'clause_list' may be + * different from the order of quals at runtime, we first sort + * 'clause_list' and then loop over 'clause_list' to estimate cost of each + * qual evaluations. + */ cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); + clause_list = order_qual_clauses(root, rel->baserestrictinfo); + + cost.startup = 0; + cost.per_tuple = 0; + pre_s = 1.0; + foreach(l, clause_list) + { + Node *qual = (Node *) lfirst(l); + List *clause_list_for_sel = NIL; + ListCell *temp_l; + cost_qual_eval_context context; + context.root = root; + context.total.startup = 0; + context.total.per_tuple = 0; + /* + * Make a temporary clause list of quals we have checked so far for + * selectivity calculation + */ + for (temp_l = list_head(clause_list); ; temp_l = lnext(temp_l)) + { + clause_list_for_sel = lappend(clause_list_for_sel, lfirst(temp_l)); + if(temp_l == l) + { + break; + } + } + + s = clauselist_selectivity(root, + clause_list_for_sel, + 0, + JOIN_INNER, + NULL); + + /* We don't charge any cost for the implicit ANDing at top level ... */ + cost_qual_eval_walker(qual, &context); + cost.startup += context.total.startup * pre_s; + cost.per_tuple += context.total.per_tuple * pre_s; + + pre_s = s; + } + + nrows = rel->tuples * pre_s; + + rel->rows = clamp_row_est(nrows); + rel->baserestrictcost = cost; set_rel_width(root, rel); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 78a1503..70a01cc 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -159,7 +159,6 @@ static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); static List *get_switched_clauses(List *clauses, Relids outerrelids); -static List *order_qual_clauses(PlannerInfo *root, List *clauses); static void copy_generic_path_info(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); static void label_sort_with_costsize(PlannerInfo *root, Sort *plan, @@ -4762,7 +4761,7 @@ get_switched_clauses(List *clauses, Relids outerrelids) * instead of bare clauses. This is another reason why trying to consider * selectivity in the ordering would likely do the wrong thing. */ -static List * +List * order_qual_clauses(PlannerInfo *root, List *clauses) { typedef struct diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index f1d16cf..016fb12 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -64,6 +64,8 @@ extern Agg *make_agg(List *tlist, List *qual, List *groupingSets, List *chain, double dNumGroups, Plan *lefttree); extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount); +extern List *order_qual_clauses(PlannerInfo *root, List *clauses); + /* * prototypes for plan/initsplan.c diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 2090a41..d9e7c2a 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2097,17 +2097,16 @@ SELECT * FROM v1 WHERE a=8; EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------- Update on public.t1 Update on public.t1 Update on public.t11 Update on public.t12 Update on public.t111 - -> Index Scan using t1_a_idx on public.t1 + -> Seq Scan on public.t1 Output: 100, t1.b, t1.c, t1.ctid - Index Cond: ((t1.a > 5) AND (t1.a < 7)) - Filter: ((t1.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a)) + Filter: ((t1.a > 5) AND (t1.a < 7) AND (t1.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2120,19 +2119,16 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; Output: t12_2.a -> Seq Scan on public.t111 t111_2 Output: t111_2.a - -> Index Scan using t11_a_idx on public.t11 + -> Seq Scan on public.t11 Output: 100, t11.b, t11.c, t11.d, t11.ctid - Index Cond: ((t11.a > 5) AND (t11.a < 7)) - Filter: ((t11.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t11.a) AND leakproof(t11.a)) - -> Index Scan using t12_a_idx on public.t12 + Filter: ((t11.a > 5) AND (t11.a < 7) AND (t11.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t11.a) AND leakproof(t11.a)) + -> Seq Scan on public.t12 Output: 100, t12.b, t12.c, t12.e, t12.ctid - Index Cond: ((t12.a > 5) AND (t12.a < 7)) - Filter: ((t12.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t12.a) AND leakproof(t12.a)) - -> Index Scan using t111_a_idx on public.t111 + Filter: ((t12.a > 5) AND (t12.a < 7) AND (t12.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t12.a) AND leakproof(t12.a)) + -> Seq Scan on public.t111 Output: 100, t111.b, t111.c, t111.d, t111.e, t111.ctid - Index Cond: ((t111.a > 5) AND (t111.a < 7)) - Filter: ((t111.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t111.a) AND leakproof(t111.a)) -(33 rows) + Filter: ((t111.a > 5) AND (t111.a < 7) AND (t111.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t111.a) AND leakproof(t111.a)) +(29 rows) UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6; SELECT * FROM v1 WHERE a=100; -- Nothing should have been changed to 100 @@ -2147,17 +2143,16 @@ SELECT * FROM t1 WHERE a=100; -- Nothing should have been changed to 100 EXPLAIN (VERBOSE, COSTS OFF) UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; - QUERY PLAN ---------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------- Update on public.t1 Update on public.t1 Update on public.t11 Update on public.t12 Update on public.t111 - -> Index Scan using t1_a_idx on public.t1 + -> Seq Scan on public.t1 Output: (t1.a + 1), t1.b, t1.c, t1.ctid - Index Cond: ((t1.a > 5) AND (t1.a = 8)) - Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a)) + Filter: ((t1.a > 5) AND (t1.a = 8) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a)) SubPlan 1 -> Append -> Seq Scan on public.t12 t12_1 @@ -2170,19 +2165,16 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; Output: t12_2.a -> Seq Scan on public.t111 t111_2 Output: t111_2.a - -> Index Scan using t11_a_idx on public.t11 + -> Seq Scan on public.t11 Output: (t11.a + 1), t11.b, t11.c, t11.d, t11.ctid - Index Cond: ((t11.a > 5) AND (t11.a = 8)) - Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t11.a) AND leakproof(t11.a)) - -> Index Scan using t12_a_idx on public.t12 + Filter: ((t11.a > 5) AND (t11.a = 8) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t11.a) AND leakproof(t11.a)) + -> Seq Scan on public.t12 Output: (t12.a + 1), t12.b, t12.c, t12.e, t12.ctid - Index Cond: ((t12.a > 5) AND (t12.a = 8)) - Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t12.a) AND leakproof(t12.a)) - -> Index Scan using t111_a_idx on public.t111 + Filter: ((t12.a > 5) AND (t12.a = 8) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t12.a) AND leakproof(t12.a)) + -> Seq Scan on public.t111 Output: (t111.a + 1), t111.b, t111.c, t111.d, t111.e, t111.ctid - Index Cond: ((t111.a > 5) AND (t111.a = 8)) - Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t111.a) AND leakproof(t111.a)) -(33 rows) + Filter: ((t111.a > 5) AND (t111.a = 8) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t111.a) AND leakproof(t111.a)) +(29 rows) UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8; NOTICE: snooped value: 8