Make estimation of mergejoin scan selectivities more robust, per recent
authorTom Lane <[email protected]>
Wed, 22 Jan 2003 20:17:07 +0000 (20:17 +0000)
committerTom Lane <[email protected]>
Wed, 22 Jan 2003 20:17:07 +0000 (20:17 +0000)
example from RaÇl GutiÅrrez.

src/backend/optimizer/path/costsize.c
src/backend/utils/adt/selfuncs.c

index 9f987a4395b8eebd7b3d265eff38eceed875d2a8..8b35a064753d6c8dcd744d70e66236b3d44f8e24 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.90 2002/09/04 20:31:20 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.90.2.1 2003/01/22 20:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -645,8 +645,22 @@ cost_mergejoin(Path *path, Query *root,
        innerscansel = firstclause->left_mergescansel;
    }
 
-   outer_rows = outer_path->parent->rows * outerscansel;
-   inner_rows = inner_path->parent->rows * innerscansel;
+   /* convert selectivity to row count; must scan at least one row */
+
+   outer_rows = ceil(outer_path->parent->rows * outerscansel);
+   if (outer_rows < 1)
+       outer_rows = 1;
+   inner_rows = ceil(inner_path->parent->rows * innerscansel);
+   if (inner_rows < 1)
+       inner_rows = 1;
+
+   /*
+    * Readjust scan selectivities to account for above rounding.  This is
+    * normally an insignificant effect, but when there are only a few rows
+    * in the inputs, failing to do this makes for a large percentage error.
+    */
+   outerscansel = outer_rows / outer_path->parent->rows;
+   innerscansel = inner_rows / inner_path->parent->rows;
 
    /* cost of source data */
 
index 211ccbf87bd397fed882d572b91bf216db151135..b4216b165d84fab7f36848c808adafe25d16ded2 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.119 2002/10/19 02:56:16 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.119.2.1 2003/01/22 20:17:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1740,7 +1740,9 @@ mergejoinscansel(Query *root, Node *clause,
                rsortop,
                ltop,
                gtop,
-               revltop;
+               leop,
+               revgtop,
+               revleop;
    Datum       leftmax,
                rightmax;
    double      selec;
@@ -1779,35 +1781,49 @@ mergejoinscansel(Query *root, Node *clause,
    /* Look up the "left < right" and "left > right" operators */
    op_mergejoin_crossops(opno, &ltop, &gtop, NULL, NULL);
 
-   /* Look up the "right < left" operator */
-   revltop = get_commutator(gtop);
-   if (!OidIsValid(revltop))
-       return;                 /* shouldn't happen */
+   /* Look up the "left <= right" operator */
+   leop = get_negator(gtop);
+   if (!OidIsValid(leop))
+       return;                 /* insufficient info in catalogs */
+
+   /* Look up the "right > left" operator */
+   revgtop = get_commutator(ltop);
+   if (!OidIsValid(revgtop))
+       return;                 /* insufficient info in catalogs */
+
+   /* Look up the "right <= left" operator */
+   revleop = get_negator(revgtop);
+   if (!OidIsValid(revleop))
+       return;                 /* insufficient info in catalogs */
 
    /*
     * Now, the fraction of the left variable that will be scanned is the
     * fraction that's <= the right-side maximum value.  But only believe
     * non-default estimates, else stick with our 1.0.
     */
-   selec = scalarineqsel(root, ltop, false, left,
+   selec = scalarineqsel(root, leop, false, left,
                          rightmax, right->vartype);
    if (selec != DEFAULT_INEQ_SEL)
        *leftscan = selec;
 
    /* And similarly for the right variable. */
-   selec = scalarineqsel(root, revltop, false, right,
+   selec = scalarineqsel(root, revleop, false, right,
                          leftmax, left->vartype);
    if (selec != DEFAULT_INEQ_SEL)
        *rightscan = selec;
 
    /*
     * Only one of the two fractions can really be less than 1.0; believe
-    * the smaller estimate and reset the other one to exactly 1.0.
+    * the smaller estimate and reset the other one to exactly 1.0.  If we
+    * get exactly equal estimates (as can easily happen with self-joins),
+    * believe neither.
     */
    if (*leftscan > *rightscan)
        *leftscan = 1.0;
-   else
+   else if (*leftscan < *rightscan)
        *rightscan = 1.0;
+   else
+       *leftscan = *rightscan = 1.0;
 }
 
 /*