]> granicus.if.org Git - postgresql/commitdiff
Make estimation of mergejoin scan selectivities more robust, per recent
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 22 Jan 2003 20:16:42 +0000 (20:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 22 Jan 2003 20:16:42 +0000 (20:16 +0000)
example from RaÇl GutiÅrrez.

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

index 5146517132fecb419c4295a4a2a40e6af0293ece..d0df5cab113f0d7073ca7c9e391c63d9d500e4ee 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.101 2003/01/20 18:54:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.102 2003/01/22 20:16:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -791,8 +791,22 @@ cost_mergejoin(Path *path, Query *root,
                innerscansel = firstclause->left_mergescansel;
        }
 
+       /* 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 42ad9f5f94bf8f36687d834f2ddad02a7090f62a..20d353a0a5099f88efd517e2d50c4d6bdb390459 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.127 2003/01/20 18:54:59 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.128 2003/01/22 20:16:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1742,7 +1742,9 @@ mergejoinscansel(Query *root, Node *clause,
                                rsortop,
                                ltop,
                                gtop,
-                               revltop;
+                               leop,
+                               revgtop,
+                               revleop;
        Datum           leftmax,
                                rightmax;
        double          selec;
@@ -1780,35 +1782,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;
 }
 
 /*