Commit fd791e7b authored by Tom Lane's avatar Tom Lane

When a relation has been proven empty by constraint exclusion, propagate that

knowledge up through any joins it participates in.  We were doing that already
in some special cases but not in the general case.  Also, defend against zero
row estimates for the input relations in cost_mergejoin --- this fix may have
eliminated the only scenario in which that can happen, but be safe.  Per
report from Alex Solovey.
parent 2a346725
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -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 * 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 * 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 static void
set_dummy_rel_pathlist(RelOptInfo *rel) set_dummy_rel_pathlist(RelOptInfo *rel)
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1385,6 +1385,12 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
Selectivity joininfactor; Selectivity joininfactor;
Path sort_path; /* dummy for result of cost_sort */ 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) if (!enable_mergejoin)
startup_cost += disable_cost; startup_cost += disable_cost;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -837,11 +837,9 @@ best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
/* /*
* Check to see if child was rejected by constraint exclusion. If so, * 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 * it will have a cheapest_total_path that's a "dummy" path.
* members (see set_plain_rel_pathlist).
*/ */
if (IsA(childrel->cheapest_total_path, AppendPath) && if (IS_DUMMY_PATH(childrel->cheapest_total_path))
((AppendPath *) childrel->cheapest_total_path)->subpaths == NIL)
continue; /* OK, we can ignore it */ continue; /* OK, we can ignore it */
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -27,6 +27,8 @@ static List *make_rels_by_clauseless_joins(PlannerInfo *root,
ListCell *other_rels); ListCell *other_rels);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel); static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(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) ...@@ -571,35 +573,75 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
&restrictlist); &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) switch (jointype)
{ {
case JOIN_INNER: 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, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER,
restrictlist); restrictlist);
break; break;
case JOIN_LEFT: case JOIN_LEFT:
if (is_dummy_rel(rel1))
{
mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT,
restrictlist); restrictlist);
break; break;
case JOIN_FULL: 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, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL,
restrictlist); restrictlist);
break; break;
case JOIN_RIGHT: case JOIN_RIGHT:
if (is_dummy_rel(rel2))
{
mark_dummy_join(joinrel);
break;
}
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
restrictlist); restrictlist);
break; break;
case JOIN_IN: 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, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_IN,
restrictlist); restrictlist);
/* REVERSE_IN isn't supported by joinpath.c */ /* REVERSE_IN isn't supported by joinpath.c */
...@@ -609,6 +651,11 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) ...@@ -609,6 +651,11 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
restrictlist); restrictlist);
break; break;
case JOIN_REVERSE_IN: 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 */ /* REVERSE_IN isn't supported by joinpath.c */
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_IN, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_IN,
restrictlist); restrictlist);
...@@ -618,12 +665,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) ...@@ -618,12 +665,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
restrictlist); restrictlist);
break; break;
case JOIN_UNIQUE_OUTER: 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, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_INNER, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_INNER,
restrictlist); restrictlist);
break; break;
case JOIN_UNIQUE_INNER: 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, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER,
restrictlist); restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER,
...@@ -883,3 +940,39 @@ has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel) ...@@ -883,3 +940,39 @@ has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel)
return false; 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);
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -693,6 +693,8 @@ typedef struct TidPath
* *
* Note: it is possible for "subpaths" to contain only one, or even no, * Note: it is possible for "subpaths" to contain only one, or even no,
* elements. These cases are optimized during create_append_plan. * 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 typedef struct AppendPath
{ {
...@@ -700,6 +702,9 @@ typedef struct AppendPath ...@@ -700,6 +702,9 @@ typedef struct AppendPath
List *subpaths; /* list of component Paths */ List *subpaths; /* list of component Paths */
} AppendPath; } 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 * ResultPath represents use of a Result plan node to compute a variable-free
* targetlist with no underlying tables (a "SELECT expressions" query). * targetlist with no underlying tables (a "SELECT expressions" query).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment