diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index ef0b25bf5e2..adbf95b31de 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.168 2008/01/11 04:02:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.169 2008/03/24 21:53:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -428,7 +428,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, * Build a dummy path for a relation that's been excluded by constraints * * Rather than inventing a special "dummy" path type, we represent this as an - * AppendPath with no members. + * AppendPath with no members (see also IS_DUMMY_PATH macro). */ static void set_dummy_rel_pathlist(RelOptInfo *rel) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index f2009fa66c3..bbb1d3a1adf 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.191 2008/01/01 19:45:50 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.192 2008/03/24 21:53:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1385,6 +1385,12 @@ cost_mergejoin(MergePath *path, PlannerInfo *root) Selectivity joininfactor; Path sort_path; /* dummy for result of cost_sort */ + /* Protect some assumptions below that rowcounts aren't zero */ + if (outer_path_rows <= 0) + outer_path_rows = 1; + if (inner_path_rows <= 0) + inner_path_rows = 1; + if (!enable_mergejoin) startup_cost += disable_cost; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index d5e2506d7a0..3e646d10325 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.115 2008/01/09 20:42:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.116 2008/03/24 21:53:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -837,11 +837,9 @@ best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel, /* * Check to see if child was rejected by constraint exclusion. If so, - * it will have a cheapest_total_path that's an Append path with no - * members (see set_plain_rel_pathlist). + * it will have a cheapest_total_path that's a "dummy" path. */ - if (IsA(childrel->cheapest_total_path, AppendPath) && - ((AppendPath *) childrel->cheapest_total_path)->subpaths == NIL) + if (IS_DUMMY_PATH(childrel->cheapest_total_path)) continue; /* OK, we can ignore it */ /* diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 8df5c21f1fd..f63c3ab43ff 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.91 2008/01/11 04:02:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.92 2008/03/24 21:53:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,8 @@ static List *make_rels_by_clauseless_joins(PlannerInfo *root, ListCell *other_rels); static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel); static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel); +static bool is_dummy_rel(RelOptInfo *rel); +static void mark_dummy_join(RelOptInfo *rel); /* @@ -571,35 +573,75 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) &restrictlist); /* - * Consider paths using each rel as both outer and inner. + * If we've already proven this join is empty, we needn't consider + * any more paths for it. + */ + if (is_dummy_rel(joinrel)) + { + bms_free(joinrelids); + return joinrel; + } + + /* + * Consider paths using each rel as both outer and inner. Depending + * on the join type, a provably empty outer or inner rel might mean + * the join is provably empty too; in which case throw away any + * previously computed paths and mark the join as dummy. (We do it + * this way since it's conceivable that dummy-ness of a multi-element + * join might only be noticeable for certain construction paths.) */ switch (jointype) { case JOIN_INNER: + if (is_dummy_rel(rel1) || is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER, restrictlist); break; case JOIN_LEFT: + if (is_dummy_rel(rel1)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT, restrictlist); break; case JOIN_FULL: + if (is_dummy_rel(rel1) && is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL, restrictlist); break; case JOIN_RIGHT: + if (is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT, restrictlist); break; case JOIN_IN: + if (is_dummy_rel(rel1) || is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_IN, restrictlist); /* REVERSE_IN isn't supported by joinpath.c */ @@ -609,6 +651,11 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) restrictlist); break; case JOIN_REVERSE_IN: + if (is_dummy_rel(rel1) || is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } /* REVERSE_IN isn't supported by joinpath.c */ add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_IN, restrictlist); @@ -618,12 +665,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) restrictlist); break; case JOIN_UNIQUE_OUTER: + if (is_dummy_rel(rel1) || is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_INNER, restrictlist); break; case JOIN_UNIQUE_INNER: + if (is_dummy_rel(rel1) || is_dummy_rel(rel2)) + { + mark_dummy_join(joinrel); + break; + } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER, restrictlist); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER, @@ -883,3 +940,39 @@ has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel) return false; } + + +/* + * is_dummy_rel --- has relation been proven empty? + * + * If so, it will have a single path that is dummy. + */ +static bool +is_dummy_rel(RelOptInfo *rel) +{ + return (rel->cheapest_total_path != NULL && + IS_DUMMY_PATH(rel->cheapest_total_path)); +} + +/* + * Mark a joinrel as proven empty. + */ +static void +mark_dummy_join(RelOptInfo *rel) +{ + /* Set dummy size estimate */ + rel->rows = 0; + + /* Evict any previously chosen paths */ + rel->pathlist = NIL; + + /* Set up the dummy path */ + add_path(rel, (Path *) create_append_path(rel, NIL)); + + /* + * Although set_cheapest will be done again later, we do it immediately + * in order to keep is_dummy_rel as cheap as possible (ie, not have + * to examine the pathlist). + */ + set_cheapest(rel); +} diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index b106c590e3e..9e9e7afbc84 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.154 2008/01/11 04:02:18 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.155 2008/03/24 21:53:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -693,6 +693,8 @@ typedef struct TidPath * * Note: it is possible for "subpaths" to contain only one, or even no, * elements. These cases are optimized during create_append_plan. + * In particular, an AppendPath with no subpaths is a "dummy" path that + * is created to represent the case that a relation is provably empty. */ typedef struct AppendPath { @@ -700,6 +702,9 @@ typedef struct AppendPath List *subpaths; /* list of component Paths */ } AppendPath; +#define IS_DUMMY_PATH(p) \ + (IsA((p), AppendPath) && ((AppendPath *) (p))->subpaths == NIL) + /* * ResultPath represents use of a Result plan node to compute a variable-free * targetlist with no underlying tables (a "SELECT expressions" query).