]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/selfuncs.c
Clean up possibly-uninitialized-variable warnings reported by gcc 4.x.
[postgresql] / src / backend / utils / adt / selfuncs.c
index 7b020ff4895141b3f6f0268382799cfbff9a6cfc..e987a66a1cf09e2f68aa5a4da5657357e58885d9 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.170 2005/01/28 20:34:25 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.189 2005/09/24 22:54:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,7 @@
  *
  * The call convention for a restriction estimator (oprrest function) is
  *
- *             Selectivity oprrest (Query *root,
+ *             Selectivity oprrest (PlannerInfo *root,
  *                                                      Oid operator,
  *                                                      List *args,
  *                                                      int varRelid);
@@ -59,7 +59,7 @@
  * except that varRelid is not needed, and instead the join type is
  * supplied:
  *
- *             Selectivity oprjoin (Query *root,
+ *             Selectivity oprjoin (PlannerInfo *root,
  *                                                      Oid operator,
  *                                                      List *args,
  *                                                      JoinType jointype);
@@ -79,7 +79,6 @@
 #include "access/heapam.h"
 #include "access/nbtree.h"
 #include "access/tuptoaster.h"
-#include "catalog/catname.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "utils/datum.h"
 #include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/nabstime.h"
 #include "utils/pg_locale.h"
 #include "utils/selfuncs.h"
 #include "utils/syscache.h"
@@ -118,6 +118,7 @@ typedef struct
        RelOptInfo *rel;                        /* Relation, or NULL if not identifiable */
        HeapTuple       statsTuple;             /* pg_statistic tuple, or NULL if none */
        /* NB: if statsTuple!=NULL, it must be freed when caller is done */
+       Oid                     vartype;                /* exposed type of expression */
        Oid                     atttype;                /* type to pass to get_attstatsslot */
        int32           atttypmod;              /* typmod to pass to get_attstatsslot */
        bool            isunique;               /* true if matched to a unique index */
@@ -134,11 +135,11 @@ static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                                  Datum lobound, Datum hibound, Oid boundstypid,
                                  double *scaledlobound, double *scaledhibound);
 static double convert_numeric_to_scalar(Datum value, Oid typid);
-static void convert_string_to_scalar(unsigned char *value,
+static void convert_string_to_scalar(char *value,
                                                 double *scaledvalue,
-                                                unsigned char *lobound,
+                                                char *lobound,
                                                 double *scaledlobound,
-                                                unsigned char *hibound,
+                                                char *hibound,
                                                 double *scaledhibound);
 static void convert_bytea_to_scalar(Datum value,
                                                double *scaledvalue,
@@ -146,24 +147,24 @@ static void convert_bytea_to_scalar(Datum value,
                                                double *scaledlobound,
                                                Datum hibound,
                                                double *scaledhibound);
-static double convert_one_string_to_scalar(unsigned char *value,
+static double convert_one_string_to_scalar(char *value,
                                                         int rangelo, int rangehi);
 static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
                                                        int rangelo, int rangehi);
-static unsigned char *convert_string_datum(Datum value, Oid typid);
+static char *convert_string_datum(Datum value, Oid typid);
 static double convert_timevalue_to_scalar(Datum value, Oid typid);
-static bool get_restriction_variable(Query *root, List *args, int varRelid,
+static bool get_restriction_variable(PlannerInfo *root, List *args, int varRelid,
                                                 VariableStatData *vardata, Node **other,
                                                 bool *varonleft);
-static void get_join_variables(Query *root, List *args,
+static void get_join_variables(PlannerInfo *root, List *args,
                                   VariableStatData *vardata1,
                                   VariableStatData *vardata2);
-static void examine_variable(Query *root, Node *node, int varRelid,
+static void examine_variable(PlannerInfo *root, Node *node, int varRelid,
                                 VariableStatData *vardata);
 static double get_variable_numdistinct(VariableStatData *vardata);
-static bool get_variable_maximum(Query *root, VariableStatData *vardata,
+static bool get_variable_maximum(PlannerInfo *root, VariableStatData *vardata,
                                         Oid sortop, Datum *max);
-static Selectivity prefix_selectivity(Query *root, VariableStatData *vardata,
+static Selectivity prefix_selectivity(PlannerInfo *root, Node *variable,
                                   Oid opclass, Const *prefix);
 static Selectivity pattern_selectivity(Const *patt, Pattern_Type ptype);
 static Datum string_to_datum(const char *str, Oid datatype);
@@ -182,7 +183,7 @@ static Const *string_to_bytea_const(const char *str, size_t str_len);
 Datum
 eqsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        int                     varRelid = PG_GETARG_INT32(3);
@@ -377,7 +378,7 @@ eqsel(PG_FUNCTION_ARGS)
 Datum
 neqsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        int                     varRelid = PG_GETARG_INT32(3);
@@ -420,7 +421,7 @@ neqsel(PG_FUNCTION_ARGS)
  * it will return a default estimate.
  */
 static double
-scalarineqsel(Query *root, Oid operator, bool isgt,
+scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
                          VariableStatData *vardata, Datum constval, Oid consttype)
 {
        Form_pg_statistic stats;
@@ -546,7 +547,7 @@ scalarineqsel(Query *root, Oid operator, bool isgt,
                                         */
                                        if (convert_to_scalar(constval, consttype, &val,
                                                                                  values[i - 1], values[i],
-                                                                                 vardata->atttype,
+                                                                                 vardata->vartype,
                                                                                  &low, &high))
                                        {
                                                if (high <= low)
@@ -652,7 +653,7 @@ scalarineqsel(Query *root, Oid operator, bool isgt,
 Datum
 scalarltsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        int                     varRelid = PG_GETARG_INT32(3);
@@ -728,7 +729,7 @@ scalarltsel(PG_FUNCTION_ARGS)
 Datum
 scalargtsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        int                     varRelid = PG_GETARG_INT32(3);
@@ -804,7 +805,7 @@ scalargtsel(PG_FUNCTION_ARGS)
 static double
 patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
 
 #ifdef NOT_USED
        Oid                     operator = PG_GETARG_OID(1);
@@ -812,6 +813,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
        List       *args = (List *) PG_GETARG_POINTER(2);
        int                     varRelid = PG_GETARG_INT32(3);
        VariableStatData vardata;
+       Node       *variable;
        Node       *other;
        bool            varonleft;
        Datum           constval;
@@ -836,6 +838,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
                ReleaseVariableStats(vardata);
                return DEFAULT_MATCH_SEL;
        }
+       variable = (Node *) linitial(args);
 
        /*
         * If the constant is NULL, assume operator is strict and return zero,
@@ -862,23 +865,18 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
        }
 
        /*
-        * The var, on the other hand, might be a binary-compatible type;
-        * particularly a domain.  Try to fold it if it's not recognized
-        * immediately.
-        */
-       vartype = vardata.atttype;
-       if (vartype != consttype)
-               vartype = getBaseType(vartype);
-
-       /*
-        * We should now be able to recognize the var's datatype.  Choose the
-        * index opclass from which we must draw the comparison operators.
+        * Similarly, the exposed type of the left-hand side should be one
+        * of those we know.  (Do not look at vardata.atttype, which might be
+        * something binary-compatible but different.)  We can use it to choose
+        * the index opclass from which we must draw the comparison operators.
         *
         * NOTE: It would be more correct to use the PATTERN opclasses than the
         * simple ones, but at the moment ANALYZE will not generate statistics
         * for the PATTERN operators.  But our results are so approximate
         * anyway that it probably hardly matters.
         */
+       vartype = vardata.vartype;
+
        switch (vartype)
        {
                case TEXTOID:
@@ -944,7 +942,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 
                if (eqopr == InvalidOid)
                        elog(ERROR, "no = operator for opclass %u", opclass);
-               eqargs = list_make2(vardata.var, prefix);
+               eqargs = list_make2(variable, prefix);
                result = DatumGetFloat8(DirectFunctionCall4(eqsel,
                                                                                                        PointerGetDatum(root),
                                                                                                 ObjectIdGetDatum(eqopr),
@@ -963,7 +961,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
                Selectivity selec;
 
                if (pstatus == Pattern_Prefix_Partial)
-                       prefixsel = prefix_selectivity(root, &vardata, opclass, prefix);
+                       prefixsel = prefix_selectivity(root, variable, opclass, prefix);
                else
                        prefixsel = 1.0;
                restsel = pattern_selectivity(rest, ptype);
@@ -1076,7 +1074,7 @@ icnlikesel(PG_FUNCTION_ARGS)
  *             booltestsel             - Selectivity of BooleanTest Node.
  */
 Selectivity
-booltestsel(Query *root, BoolTestType booltesttype, Node *arg,
+booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg,
                        int varRelid, JoinType jointype)
 {
        VariableStatData vardata;
@@ -1241,7 +1239,8 @@ booltestsel(Query *root, BoolTestType booltesttype, Node *arg,
  *             nulltestsel             - Selectivity of NullTest Node.
  */
 Selectivity
-nulltestsel(Query *root, NullTestType nulltesttype, Node *arg, int varRelid)
+nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
+                       Node *arg, int varRelid)
 {
        VariableStatData vardata;
        double          selec;
@@ -1313,7 +1312,7 @@ nulltestsel(Query *root, NullTestType nulltesttype, Node *arg, int varRelid)
 Datum
 eqjoinsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        JoinType        jointype = (JoinType) PG_GETARG_INT16(3);
@@ -1573,7 +1572,7 @@ eqjoinsel(PG_FUNCTION_ARGS)
 Datum
 neqjoinsel(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
        Oid                     operator = PG_GETARG_OID(1);
        List       *args = (List *) PG_GETARG_POINTER(2);
        JoinType        jointype = (JoinType) PG_GETARG_INT16(3);
@@ -1723,7 +1722,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
  * variable.
  */
 void
-mergejoinscansel(Query *root, Node *clause,
+mergejoinscansel(PlannerInfo *root, Node *clause,
                                 Selectivity *leftscan,
                                 Selectivity *rightscan)
 {
@@ -1844,7 +1843,7 @@ typedef struct
 } GroupVarInfo;
 
 static List *
-add_unique_group_var(Query *root, List *varinfos,
+add_unique_group_var(PlannerInfo *root, List *varinfos,
                                         Node *var, VariableStatData *vardata)
 {
        GroupVarInfo *varinfo;
@@ -1956,7 +1955,7 @@ add_unique_group_var(Query *root, List *varinfos,
  * do better).
  */
 double
-estimate_num_groups(Query *root, List *groupExprs, double input_rows)
+estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows)
 {
        List       *varinfos = NIL;
        double          numdistinct;
@@ -2043,6 +2042,7 @@ estimate_num_groups(Query *root, List *groupExprs, double input_rows)
                GroupVarInfo *varinfo1 = (GroupVarInfo *) linitial(varinfos);
                RelOptInfo *rel = varinfo1->rel;
                double          reldistinct = varinfo1->ndistinct;
+               double          relmaxndistinct = reldistinct;
                int                     relvarcount = 1;
                List       *newvarinfos = NIL;
 
@@ -2057,6 +2057,8 @@ estimate_num_groups(Query *root, List *groupExprs, double input_rows)
                        if (varinfo2->rel == varinfo1->rel)
                        {
                                reldistinct *= varinfo2->ndistinct;
+                               if (relmaxndistinct < varinfo2->ndistinct)
+                                       relmaxndistinct = varinfo2->ndistinct;
                                relvarcount++;
                        }
                        else
@@ -2075,12 +2077,23 @@ estimate_num_groups(Query *root, List *groupExprs, double input_rows)
                        /*
                         * Clamp to size of rel, or size of rel / 10 if multiple Vars.
                         * The fudge factor is because the Vars are probably correlated
-                        * but we don't know by how much.
+                        * but we don't know by how much.  We should never clamp to less
+                        * than the largest ndistinct value for any of the Vars, though,
+                        * since there will surely be at least that many groups.
                         */
                        double          clamp = rel->tuples;
 
                        if (relvarcount > 1)
+                       {
                                clamp *= 0.1;
+                               if (clamp < relmaxndistinct)
+                               {
+                                       clamp = relmaxndistinct;
+                                       /* for sanity in case some ndistinct is too large: */
+                                       if (clamp > rel->tuples)
+                                               clamp = rel->tuples;
+                               }
+                       }
                        if (reldistinct > clamp)
                                reldistinct = clamp;
 
@@ -2140,7 +2153,7 @@ estimate_num_groups(Query *root, List *groupExprs, double input_rows)
  * inner rel is well-dispersed (or the alternatives seem much worse).
  */
 Selectivity
-estimate_hash_bucketsize(Query *root, Node *hashkey, int nbuckets)
+estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets)
 {
        VariableStatData vardata;
        double          estfract,
@@ -2198,8 +2211,8 @@ estimate_hash_bucketsize(Query *root, Node *hashkey, int nbuckets)
         * the number of buckets is less than the expected number of distinct
         * values; otherwise it is 1/ndistinct.
         */
-       if (ndistinct > (double) nbuckets)
-               estfract = 1.0 / (double) nbuckets;
+       if (ndistinct > nbuckets)
+               estfract = 1.0 / nbuckets;
        else
                estfract = 1.0 / ndistinct;
 
@@ -2290,19 +2303,20 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                                  double *scaledlobound, double *scaledhibound)
 {
        /*
-        * In present usage, we can assume that the valuetypid exactly matches
-        * the declared input type of the operator we are invoked for (because
-        * constant-folding will ensure that any Const passed to the operator
-        * has been reduced to the correct type).  However, the boundstypid is
-        * the type of some variable that might be only binary-compatible with
-        * the declared type; in particular it might be a domain type.  Must
-        * fold the variable type down to base type so we can recognize it.
-        * (But we can skip that lookup if the variable type matches the
-        * const.)
+        * Both the valuetypid and the boundstypid should exactly match
+        * the declared input type(s) of the operator we are invoked for,
+        * so we just error out if either is not recognized.
+        *
+        * XXX The histogram we are interpolating between points of could belong
+        * to a column that's only binary-compatible with the declared type.
+        * In essence we are assuming that the semantics of binary-compatible
+        * types are enough alike that we can use a histogram generated with one
+        * type's operators to estimate selectivity for the other's.  This is
+        * outright wrong in some cases --- in particular signed versus unsigned
+        * interpretation could trip us up.  But it's useful enough in the
+        * majority of cases that we do it anyway.  Should think about more
+        * rigorous ways to do it.
         */
-       if (boundstypid != valuetypid)
-               boundstypid = getBaseType(boundstypid);
-
        switch (valuetypid)
        {
                        /*
@@ -2336,9 +2350,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                case TEXTOID:
                case NAMEOID:
                        {
-                               unsigned char *valstr = convert_string_datum(value, valuetypid);
-                               unsigned char *lostr = convert_string_datum(lobound, boundstypid);
-                               unsigned char *histr = convert_string_datum(hibound, boundstypid);
+                               char *valstr = convert_string_datum(value, valuetypid);
+                               char *lostr = convert_string_datum(lobound, boundstypid);
+                               char *histr = convert_string_datum(hibound, boundstypid);
 
                                convert_string_to_scalar(valstr, scaledvalue,
                                                                                 lostr, scaledlobound,
@@ -2389,6 +2403,7 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                        return true;
        }
        /* Don't know how to convert */
+       *scaledvalue = *scaledlobound = *scaledhibound = 0;
        return false;
 }
 
@@ -2457,31 +2472,31 @@ convert_numeric_to_scalar(Datum value, Oid typid)
  * so this is more likely to happen than you might think.)
  */
 static void
-convert_string_to_scalar(unsigned char *value,
+convert_string_to_scalar(char *value,
                                                 double *scaledvalue,
-                                                unsigned char *lobound,
+                                                char *lobound,
                                                 double *scaledlobound,
-                                                unsigned char *hibound,
+                                                char *hibound,
                                                 double *scaledhibound)
 {
        int                     rangelo,
                                rangehi;
-       unsigned char *sptr;
+       char       *sptr;
 
-       rangelo = rangehi = hibound[0];
+       rangelo = rangehi = (unsigned char) hibound[0];
        for (sptr = lobound; *sptr; sptr++)
        {
-               if (rangelo > *sptr)
-                       rangelo = *sptr;
-               if (rangehi < *sptr)
-                       rangehi = *sptr;
+               if (rangelo > (unsigned char) *sptr)
+                       rangelo = (unsigned char) *sptr;
+               if (rangehi < (unsigned char) *sptr)
+                       rangehi = (unsigned char) *sptr;
        }
        for (sptr = hibound; *sptr; sptr++)
        {
-               if (rangelo > *sptr)
-                       rangelo = *sptr;
-               if (rangehi < *sptr)
-                       rangehi = *sptr;
+               if (rangelo > (unsigned char) *sptr)
+                       rangelo = (unsigned char) *sptr;
+               if (rangehi < (unsigned char) *sptr)
+                       rangehi = (unsigned char) *sptr;
        }
        /* If range includes any upper-case ASCII chars, make it include all */
        if (rangelo <= 'Z' && rangehi >= 'A')
@@ -2537,9 +2552,9 @@ convert_string_to_scalar(unsigned char *value,
 }
 
 static double
-convert_one_string_to_scalar(unsigned char *value, int rangelo, int rangehi)
+convert_one_string_to_scalar(char *value, int rangelo, int rangehi)
 {
-       int                     slen = strlen((char *) value);
+       int                     slen = strlen(value);
        double          num,
                                denom,
                                base;
@@ -2560,7 +2575,7 @@ convert_one_string_to_scalar(unsigned char *value, int rangelo, int rangehi)
        denom = base;
        while (slen-- > 0)
        {
-               int                     ch = *value++;
+               int                     ch = (unsigned char) *value++;
 
                if (ch < rangelo)
                        ch = rangelo - 1;
@@ -2579,7 +2594,7 @@ convert_one_string_to_scalar(unsigned char *value, int rangelo, int rangehi)
  * When using a non-C locale, we must pass the string through strxfrm()
  * before continuing, so as to generate correct locale-specific results.
  */
-static unsigned char *
+static char *
 convert_string_datum(Datum value, Oid typid)
 {
        char       *val;
@@ -2646,7 +2661,7 @@ convert_string_datum(Datum value, Oid typid)
                val = xfrmstr;
        }
 
-       return (unsigned char *) val;
+       return val;
 }
 
 /*
@@ -2770,10 +2785,11 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
                                 * too accurate, but plenty good enough for our purposes.
                                 */
 #ifdef HAVE_INT64_TIMESTAMP
-                               return (interval->time + (interval->month * ((365.25 / 12.0) * 86400000000.0)));
+                               return interval->time + interval->day * (double)USECS_PER_DAY +
+                                          interval->month * ((DAYS_PER_YEAR / (double)MONTHS_PER_YEAR) * USECS_PER_DAY);
 #else
-                               return interval->time +
-                               interval  ->month * (365.25 / 12.0 * 24.0 * 60.0 * 60.0);
+                               return interval->time + interval->day * SECS_PER_DAY +
+                                               interval->month * ((DAYS_PER_YEAR / (double)MONTHS_PER_YEAR) * (double)SECS_PER_DAY);
 #endif
                        }
                case RELTIMEOID:
@@ -2784,14 +2800,14 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
 #endif
                case TINTERVALOID:
                        {
-                               TimeInterval interval = DatumGetTimeInterval(value);
+                               TimeInterval tinterval = DatumGetTimeInterval(value);
 
 #ifdef HAVE_INT64_TIMESTAMP
-                               if (interval->status != 0)
-                                       return ((interval->data[1] - interval->data[0]) *1000000.0);
+                               if (tinterval->status != 0)
+                                       return ((tinterval->data[1] - tinterval->data[0]) * 1000000.0);
 #else
-                               if (interval->status != 0)
-                                       return interval->data[1] - interval->data[0];
+                               if (tinterval->status != 0)
+                                       return tinterval->data[1] - tinterval->data[0];
 #endif
                                return 0;               /* for lack of a better idea */
                        }
@@ -2828,14 +2844,13 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
  *             and also indicate which side it was on and the other argument.
  *
  * Inputs:
- *     root: the Query
+ *     root: the planner info
  *     args: clause argument list
  *     varRelid: see specs for restriction selectivity functions
  *
  * Outputs: (these are valid only if TRUE is returned)
  *     *vardata: gets information about variable (see examine_variable)
- *     *other: gets other clause argument, stripped of binary relabeling,
- *             and aggressively reduced to a constant
+ *     *other: gets other clause argument, aggressively reduced to a constant
  *     *varonleft: set TRUE if variable is on the left, FALSE if on the right
  *
  * Returns TRUE if a variable is identified, otherwise FALSE.
@@ -2844,7 +2859,7 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
  * callers are expecting that the other side will act like a pseudoconstant.
  */
 static bool
-get_restriction_variable(Query *root, List *args, int varRelid,
+get_restriction_variable(PlannerInfo *root, List *args, int varRelid,
                                                 VariableStatData *vardata, Node **other,
                                                 bool *varonleft)
 {
@@ -2898,7 +2913,7 @@ get_restriction_variable(Query *root, List *args, int varRelid,
  *             Apply examine_variable() to each side of a join clause.
  */
 static void
-get_join_variables(Query *root, List *args,
+get_join_variables(PlannerInfo *root, List *args,
                                   VariableStatData *vardata1, VariableStatData *vardata2)
 {
        Node       *left,
@@ -2920,17 +2935,20 @@ get_join_variables(Query *root, List *args,
  *             Fill in a VariableStatData struct to describe the expression.
  *
  * Inputs:
- *     root: the Query
+ *     root: the planner info
  *     node: the expression tree to examine
  *     varRelid: see specs for restriction selectivity functions
  *
  * Outputs: *vardata is filled as follows:
- *     var: the input expression (with any binary relabeling stripped)
+ *     var: the input expression (with any binary relabeling stripped, if
+ *             it is or contains a variable; but otherwise the type is preserved)
  *     rel: RelOptInfo for relation containing variable; NULL if expression
  *             contains no Vars (NOTE this could point to a RelOptInfo of a
  *             subquery, not one in the current query).
  *     statsTuple: the pg_statistic entry for the variable, if one exists;
  *             otherwise NULL.
+ *     vartype: exposed type of the expression; this should always match
+ *             the declared input type of the operator we are estimating for.
  *     atttype, atttypmod: type data to pass to get_attstatsslot().  This is
  *             commonly the same as the exposed type of the variable argument,
  *             but can be different in binary-compatible-type cases.
@@ -2938,35 +2956,40 @@ get_join_variables(Query *root, List *args,
  * Caller is responsible for doing ReleaseVariableStats() before exiting.
  */
 static void
-examine_variable(Query *root, Node *node, int varRelid,
+examine_variable(PlannerInfo *root, Node *node, int varRelid,
                                 VariableStatData *vardata)
 {
+       Node       *basenode;
        Relids          varnos;
        RelOptInfo *onerel;
 
        /* Make sure we don't return dangling pointers in vardata */
        MemSet(vardata, 0, sizeof(VariableStatData));
 
-       /* Ignore any binary-compatible relabeling */
+       /* Save the exposed type of the expression */
+       vardata->vartype = exprType(node);
 
-       if (IsA(node, RelabelType))
-               node = (Node *) ((RelabelType *) node)->arg;
+       /* Look inside any binary-compatible relabeling */
 
-       vardata->var = node;
+       if (IsA(node, RelabelType))
+               basenode = (Node *) ((RelabelType *) node)->arg;
+       else
+               basenode = node;
 
        /* Fast path for a simple Var */
 
-       if (IsA(node, Var) &&
-               (varRelid == 0 || varRelid == ((Var *) node)->varno))
+       if (IsA(basenode, Var) &&
+               (varRelid == 0 || varRelid == ((Var *) basenode)->varno))
        {
-               Var                *var = (Var *) node;
+               Var                *var = (Var *) basenode;
                Oid                     relid;
 
+               vardata->var = basenode;        /* return Var without relabeling */
                vardata->rel = find_base_rel(root, var->varno);
                vardata->atttype = var->vartype;
                vardata->atttypmod = var->vartypmod;
 
-               relid = getrelid(var->varno, root->rtable);
+               relid = getrelid(var->varno, root->parse->rtable);
 
                if (OidIsValid(relid))
                {
@@ -2995,7 +3018,7 @@ examine_variable(Query *root, Node *node, int varRelid,
         * membership.  Note that when varRelid isn't zero, only vars of that
         * relation are considered "real" vars.
         */
-       varnos = pull_varnos(node);
+       varnos = pull_varnos(basenode);
 
        onerel = NULL;
 
@@ -3010,6 +3033,7 @@ examine_variable(Query *root, Node *node, int varRelid,
                                onerel = find_base_rel(root,
                                   (varRelid ? varRelid : bms_singleton_member(varnos)));
                                vardata->rel = onerel;
+                               node = basenode; /* strip any relabeling */
                        }
                        /* else treat it as a constant */
                        break;
@@ -3018,11 +3042,13 @@ examine_variable(Query *root, Node *node, int varRelid,
                        {
                                /* treat it as a variable of a join relation */
                                vardata->rel = find_join_rel(root, varnos);
+                               node = basenode; /* strip any relabeling */
                        }
                        else if (bms_is_member(varRelid, varnos))
                        {
                                /* ignore the vars belonging to other relations */
                                vardata->rel = find_base_rel(root, varRelid);
+                               node = basenode; /* strip any relabeling */
                                /* note: no point in expressional-index search here */
                        }
                        /* else treat it as a constant */
@@ -3031,6 +3057,7 @@ examine_variable(Query *root, Node *node, int varRelid,
 
        bms_free(varnos);
 
+       vardata->var = node;
        vardata->atttype = exprType(node);
        vardata->atttypmod = exprTypmod(node);
 
@@ -3130,7 +3157,7 @@ get_variable_numdistinct(VariableStatData *vardata)
                stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
                stadistinct = stats->stadistinct;
        }
-       else if (vardata->atttype == BOOLOID)
+       else if (vardata->vartype == BOOLOID)
        {
                /*
                 * Special-case boolean columns: presumably, two distinct values.
@@ -3227,7 +3254,7 @@ get_variable_numdistinct(VariableStatData *vardata)
  * minimum instead of the maximum, just pass the ">" operator instead.)
  */
 static bool
-get_variable_maximum(Query *root, VariableStatData *vardata,
+get_variable_maximum(PlannerInfo *root, VariableStatData *vardata,
                                         Oid sortop, Datum *max)
 {
        Datum           tmax = 0;
@@ -3661,7 +3688,7 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
  * Estimate the selectivity of a fixed prefix for a pattern match.
  *
  * A fixed prefix "foo" is estimated as the selectivity of the expression
- * "variable >= 'foo' AND variable < 'fop'" (see also indxqual.c).
+ * "variable >= 'foo' AND variable < 'fop'" (see also indxpath.c).
  *
  * We use the >= and < operators from the specified btree opclass to do the
  * estimation. The given variable and Const must be of the associated
@@ -3673,7 +3700,7 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
  * more useful to use the upper-bound code than not.
  */
 static Selectivity
-prefix_selectivity(Query *root, VariableStatData *vardata,
+prefix_selectivity(PlannerInfo *root, Node *variable,
                                   Oid opclass, Const *prefixcon)
 {
        Selectivity prefixsel;
@@ -3685,7 +3712,7 @@ prefix_selectivity(Query *root, VariableStatData *vardata,
                                                                BTGreaterEqualStrategyNumber);
        if (cmpopr == InvalidOid)
                elog(ERROR, "no >= operator for opclass %u", opclass);
-       cmpargs = list_make2(vardata->var, prefixcon);
+       cmpargs = list_make2(variable, prefixcon);
        /* Assume scalargtsel is appropriate for all supported types */
        prefixsel = DatumGetFloat8(DirectFunctionCall4(scalargtsel,
                                                                                                   PointerGetDatum(root),
@@ -3707,7 +3734,7 @@ prefix_selectivity(Query *root, VariableStatData *vardata,
                                                                        BTLessStrategyNumber);
                if (cmpopr == InvalidOid)
                        elog(ERROR, "no < operator for opclass %u", opclass);
-               cmpargs = list_make2(vardata->var, greaterstrcon);
+               cmpargs = list_make2(variable, greaterstrcon);
                /* Assume scalarltsel is appropriate for all supported types */
                topsel = DatumGetFloat8(DirectFunctionCall4(scalarltsel,
                                                                                                        PointerGetDatum(root),
@@ -3722,7 +3749,7 @@ prefix_selectivity(Query *root, VariableStatData *vardata,
                prefixsel = topsel + prefixsel - 1.0;
 
                /* Adjust for double-exclusion of NULLs */
-               prefixsel += nulltestsel(root, IS_NULL, vardata->var, 0);
+               prefixsel += nulltestsel(root, IS_NULL, variable, 0);
 
                /*
                 * A zero or slightly negative prefixsel should be converted into
@@ -4021,7 +4048,7 @@ pattern_selectivity(Const *patt, Pattern_Type ptype)
  *
  * NOTE: at present this assumes we are in the C locale, so that simple
  * bytewise comparison applies.  However, we might be in a multibyte
- * encoding such as UTF-8, so we do have to watch out for generating
+ * encoding such as UTF8, so we do have to watch out for generating
  * invalid encoding sequences.
  */
 Const *
@@ -4078,8 +4105,7 @@ make_greater_string(const Const *str_const)
                        if (datatype != BYTEAOID)
                        {
                                /* do not generate invalid encoding sequences */
-                               if (!pg_verifymbstr((const unsigned char *) workstr,
-                                                                       len, true))
+                               if (!pg_verifymbstr(workstr, len, true))
                                        continue;
                                workstr_const = string_to_const(workstr, datatype);
                        }
@@ -4098,7 +4124,7 @@ make_greater_string(const Const *str_const)
                 * byte, depending on the character encoding.
                 */
                if (datatype != BYTEAOID && pg_database_encoding_max_length() > 1)
-                       len = pg_mbcliplen((const unsigned char *) workstr, len, len - 1);
+                       len = pg_mbcliplen(workstr, len, len - 1);
                else
                        len -= 1;
 
@@ -4171,18 +4197,23 @@ string_to_bytea_const(const char *str, size_t str_len)
  * don't have any better idea about how to estimate.  Index-type-specific
  * knowledge can be incorporated in the type-specific routines.
  *
+ * One bit of index-type-specific knowledge we can relatively easily use
+ * in genericcostestimate is the estimate of the number of index tuples
+ * visited.  If numIndexTuples is not 0 then it is used as the estimate,
+ * otherwise we compute a generic estimate.
+ *
  *-------------------------------------------------------------------------
  */
 
 static void
-genericcostestimate(Query *root, RelOptInfo *rel,
+genericcostestimate(PlannerInfo *root,
                                        IndexOptInfo *index, List *indexQuals,
+                                       double numIndexTuples,
                                        Cost *indexStartupCost,
                                        Cost *indexTotalCost,
                                        Selectivity *indexSelectivity,
                                        double *indexCorrelation)
 {
-       double          numIndexTuples;
        double          numIndexPages;
        QualCost        index_qual_cost;
        double          qual_op_cost;
@@ -4197,12 +4228,11 @@ genericcostestimate(Query *root, RelOptInfo *rel,
         * of partial redundancy (such as "x < 4" from the qual and "x < 5"
         * from the predicate) will be recognized and handled correctly by
         * clauselist_selectivity().  This assumption is somewhat fragile,
-        * since it depends on pred_test() and clauselist_selectivity() having
-        * similar capabilities, and there are certainly many cases where we
-        * will end up with a too-low selectivity estimate.  This will bias
-        * the system in favor of using partial indexes where possible, which
-        * is not necessarily a bad thing.      But it'd be nice to do better
-        * someday.
+        * since it depends on predicate_implied_by() and clauselist_selectivity()
+        * having similar capabilities, and there are certainly many cases where
+        * we will end up with a too-low selectivity estimate.  This will bias the
+        * system in favor of using partial indexes where possible, which is not
+        * necessarily a bad thing. But it'd be nice to do better someday.
         *
         * Note that index->indpred and indexQuals are both in implicit-AND form,
         * so ANDing them together just takes merging the lists.  However,
@@ -4227,24 +4257,24 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 
        /* Estimate the fraction of main-table tuples that will be visited */
        *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
-                                                                                          rel->relid,
+                                                                                          index->rel->relid,
                                                                                           JOIN_INNER);
 
        /*
-        * Estimate the number of tuples that will be visited.  We do it in
-        * this rather peculiar-looking way in order to get the right answer
-        * for partial indexes.  We can bound the number of tuples by the
-        * index size, in any case.
+        * If caller didn't give us an estimate, estimate the number of index
+        * tuples that will be visited.  We do it in this rather peculiar-looking
+        * way in order to get the right answer for partial indexes.
         */
-       numIndexTuples = *indexSelectivity * rel->tuples;
-
-       if (numIndexTuples > index->tuples)
-               numIndexTuples = index->tuples;
+       if (numIndexTuples <= 0.0)
+               numIndexTuples = *indexSelectivity * index->rel->tuples;
 
        /*
-        * Always estimate at least one tuple is touched, even when
+        * We can bound the number of tuples by the index size in any case.
+        * Also, always estimate at least one tuple is touched, even when
         * indexSelectivity estimate is tiny.
         */
+       if (numIndexTuples > index->tuples)
+               numIndexTuples = index->tuples;
        if (numIndexTuples < 1.0)
                numIndexTuples = 1.0;
 
@@ -4304,26 +4334,112 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 Datum
 btcostestimate(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
-       RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
-       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
-       List       *indexQuals = (List *) PG_GETARG_POINTER(3);
-       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
-       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
-       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
-       double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
+       List       *indexQuals = (List *) PG_GETARG_POINTER(2);
+       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+       double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
        Oid                     relid;
        AttrNumber      colnum;
        HeapTuple       tuple;
+       double          numIndexTuples;
+       List       *indexBoundQuals;
+       int                     indexcol;
+       bool            eqQualHere;
+       ListCell   *l;
+
+       /*
+        * For a btree scan, only leading '=' quals plus inequality quals
+        * for the immediately next attribute contribute to index selectivity
+        * (these are the "boundary quals" that determine the starting and
+        * stopping points of the index scan).  Additional quals can suppress
+        * visits to the heap, so it's OK to count them in indexSelectivity,
+        * but they should not count for estimating numIndexTuples.  So we must
+        * examine the given indexQuals to find out which ones count as boundary
+        * quals.  We rely on the knowledge that they are given in index column
+        * order.
+        */
+       indexBoundQuals = NIL;
+       indexcol = 0;
+       eqQualHere = false;
+       foreach(l, indexQuals)
+       {
+               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+               Expr   *clause;
+               Oid             clause_op;
+               int             op_strategy;
+
+               Assert(IsA(rinfo, RestrictInfo));
+               clause = rinfo->clause;
+               Assert(IsA(clause, OpExpr));
+               clause_op = ((OpExpr *) clause)->opno;
+               if (match_index_to_operand(get_leftop(clause), indexcol, index))
+               {
+                       /* clause_op is correct */
+               }
+               else if (match_index_to_operand(get_rightop(clause), indexcol, index))
+               {
+                       /* Must flip operator to get the opclass member */
+                       clause_op = get_commutator(clause_op);
+               }
+               else
+               {
+                       /* Must be past the end of quals for indexcol, try next */
+                       if (!eqQualHere)
+                               break;                  /* done if no '=' qual for indexcol */
+                       indexcol++;
+                       eqQualHere = false;
+                       if (match_index_to_operand(get_leftop(clause), indexcol, index))
+                       {
+                               /* clause_op is correct */
+                       }
+                       else if (match_index_to_operand(get_rightop(clause),
+                                                                                       indexcol, index))
+                       {
+                               /* Must flip operator to get the opclass member */
+                               clause_op = get_commutator(clause_op);
+                       }
+                       else
+                       {
+                               /* No quals for new indexcol, so we are done */
+                               break;
+                       }
+               }
+               op_strategy = get_op_opclass_strategy(clause_op,
+                                                                                         index->classlist[indexcol]);
+               Assert(op_strategy != 0);                       /* not a member of opclass?? */
+               if (op_strategy == BTEqualStrategyNumber)
+                       eqQualHere = true;
+               indexBoundQuals = lappend(indexBoundQuals, rinfo);
+       }
+
+       /*
+        * If index is unique and we found an '=' clause for each column,
+        * we can just assume numIndexTuples = 1 and skip the expensive
+        * clauselist_selectivity calculations.
+        */
+       if (index->unique && indexcol == index->ncolumns - 1 && eqQualHere)
+               numIndexTuples = 1.0;
+       else
+       {
+               Selectivity btreeSelectivity;
+
+               btreeSelectivity = clauselist_selectivity(root, indexBoundQuals,
+                                                                                                 index->rel->relid,
+                                                                                                 JOIN_INNER);
+               numIndexTuples = btreeSelectivity * index->rel->tuples;
+       }
 
-       genericcostestimate(root, rel, index, indexQuals,
+       genericcostestimate(root, index, indexQuals, numIndexTuples,
                                                indexStartupCost, indexTotalCost,
                                                indexSelectivity, indexCorrelation);
 
        /*
         * If we can get an estimate of the first column's ordering
         * correlation C from pg_statistic, estimate the index correlation as
-        * C for a single- column index, or C * 0.75 for multiple columns.
+        * C for a single-column index, or C * 0.75 for multiple columns.
         * (The idea here is that multiple columns dilute the importance of
         * the first column's ordering, but don't negate it entirely.  Before
         * 8.0 we divided the correlation by the number of columns, but that
@@ -4332,7 +4448,7 @@ btcostestimate(PG_FUNCTION_ARGS)
        if (index->indexkeys[0] != 0)
        {
                /* Simple variable --- look to stats for the underlying table */
-               relid = getrelid(rel->relid, root->rtable);
+               relid = getrelid(index->rel->relid, root->parse->rtable);
                Assert(relid != InvalidOid);
                colnum = index->indexkeys[0];
        }
@@ -4384,16 +4500,15 @@ btcostestimate(PG_FUNCTION_ARGS)
 Datum
 rtcostestimate(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
-       RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
-       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
-       List       *indexQuals = (List *) PG_GETARG_POINTER(3);
-       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
-       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
-       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
-       double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
-
-       genericcostestimate(root, rel, index, indexQuals,
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
+       List       *indexQuals = (List *) PG_GETARG_POINTER(2);
+       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+       double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+       genericcostestimate(root, index, indexQuals, 0.0,
                                                indexStartupCost, indexTotalCost,
                                                indexSelectivity, indexCorrelation);
 
@@ -4403,16 +4518,15 @@ rtcostestimate(PG_FUNCTION_ARGS)
 Datum
 hashcostestimate(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
-       RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
-       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
-       List       *indexQuals = (List *) PG_GETARG_POINTER(3);
-       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
-       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
-       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
-       double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
-
-       genericcostestimate(root, rel, index, indexQuals,
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
+       List       *indexQuals = (List *) PG_GETARG_POINTER(2);
+       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+       double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+       genericcostestimate(root, index, indexQuals, 0.0,
                                                indexStartupCost, indexTotalCost,
                                                indexSelectivity, indexCorrelation);
 
@@ -4422,16 +4536,15 @@ hashcostestimate(PG_FUNCTION_ARGS)
 Datum
 gistcostestimate(PG_FUNCTION_ARGS)
 {
-       Query      *root = (Query *) PG_GETARG_POINTER(0);
-       RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
-       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
-       List       *indexQuals = (List *) PG_GETARG_POINTER(3);
-       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
-       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
-       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
-       double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
-
-       genericcostestimate(root, rel, index, indexQuals,
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+       IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
+       List       *indexQuals = (List *) PG_GETARG_POINTER(2);
+       Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+       Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+       Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+       double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+       genericcostestimate(root, index, indexQuals, 0.0,
                                                indexStartupCost, indexTotalCost,
                                                indexSelectivity, indexCorrelation);