]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/path/indxpath.c
Revise handling of index-type-specific indexscan cost estimation, per
[postgresql] / src / backend / optimizer / path / indxpath.c
index 4f52f9377d2045a8549920ad2f0ba344d5cb2e78..7bb4a8eaeb1d83066413fb1e9c34c8b496e2d08b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.73 1999/11/22 17:56:07 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.77 2000/01/22 23:50:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "catalog/pg_amop.h"
 #include "catalog/pg_operator.h"
 #include "executor/executor.h"
+#include "mb/pg_wchar.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+
+#define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \
+       (indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid)
+
 typedef enum {
        Prefix_None, Prefix_Partial, Prefix_Exact
 } Prefix_Status;
 
-static void match_index_orclauses(RelOptInfo *rel, RelOptInfo *index, int indexkey,
-                                         int xclass, List *restrictinfo_list);
-static List *match_index_orclause(RelOptInfo *rel, RelOptInfo *index, int indexkey,
-                        int xclass, List *or_clauses, List *other_matching_indices);
-static bool match_or_subclause_to_indexkey(RelOptInfo *rel, RelOptInfo *index,
-                                                                                  int indexkey, int xclass,
+static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
+                                                                 int indexkey, Oid opclass,
+                                                                 List *restrictinfo_list);
+static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
+                                                                 int indexkey, Oid opclass,
+                                                                 List *or_clauses,
+                                                                 List *other_matching_indices);
+static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
+                                                                                  IndexOptInfo *index,
+                                                                                  int indexkey, Oid opclass,
                                                                                   Expr *clause);
-static List *group_clauses_by_indexkey(RelOptInfo *rel, RelOptInfo *index,
-                                 int *indexkeys, Oid *classes, List *restrictinfo_list);
-static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel, RelOptInfo *index,
-                                                               int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list);
-static bool match_clause_to_indexkey(RelOptInfo *rel, RelOptInfo *index,
-                                                                        int indexkey, int xclass,
+static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
+                                                                          int *indexkeys, Oid *classes,
+                                                                          List *restrictinfo_list);
+static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel,
+                                                                                        IndexOptInfo *index,
+                                                                                        int *indexkeys, Oid *classes,
+                                                                                        List *join_cinfo_list,
+                                                                                        List *restr_cinfo_list);
+static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
+                                                                        int indexkey, Oid opclass,
                                                                         Expr *clause, bool join);
-static bool indexable_operator(Expr *clause, int xclass, Oid relam,
-                                                          bool indexkey_on_left);
 static bool pred_test(List *predicate_list, List *restrictinfo_list,
-                 List *joininfo_list);
+                                         List *joininfo_list);
 static bool one_pred_test(Expr *predicate, List *restrictinfo_list);
 static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
 static bool one_pred_clause_test(Expr *predicate, Node *clause);
 static bool clause_pred_clause_test(Expr *predicate, Node *clause);
-static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
+static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
                                                                  List *joininfo_list, List *restrictinfo_list,
                                                                  List **clausegroups, List **outerrelids);
-static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
+static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                                                         List *clausegroup_list, List *outerrelids_list);
-static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index,
+static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index,
                                                                 List *joininfo_list);
 static bool useful_for_ordering(Query *root, RelOptInfo *rel,
-                                                               RelOptInfo *index);
+                                                               IndexOptInfo *index);
 static bool match_index_to_operand(int indexkey, Var *operand,
-                                                                  RelOptInfo *rel, RelOptInfo *index);
-static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
-static bool match_special_index_operator(Expr *clause, bool indexkey_on_left);
+                                                                  RelOptInfo *rel, IndexOptInfo *index);
+static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
+                                                                  IndexOptInfo *index);
+static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
+                                                                                bool indexkey_on_left);
 static Prefix_Status like_fixed_prefix(char *patt, char **prefix);
 static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive,
                                                                                char **prefix);
 static List *prefix_quals(Var *leftop, Oid expr_op,
                                                  char *prefix, Prefix_Status pstatus);
+static char *make_greater_string(const char * str, Oid datatype);
+static Oid find_operator(const char * opname, Oid datatype);
+static Datum string_to_datum(const char * str, Oid datatype);
+static Const *string_to_const(const char * str, Oid datatype);
+static bool string_lessthan(const char * str1, const char * str2,
+                                                       Oid datatype);
 
 
 /*
@@ -130,7 +149,7 @@ create_index_paths(Query *root,
 
        foreach(ilist, indices)
        {
-               RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
+               IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
                List       *restrictclauses;
                List       *joinclausegroups;
                List       *joinouterrelids;
@@ -253,9 +272,9 @@ create_index_paths(Query *root,
  */
 static void
 match_index_orclauses(RelOptInfo *rel,
-                                         RelOptInfo *index,
+                                         IndexOptInfo *index,
                                          int indexkey,
-                                         int xclass,
+                                         Oid opclass,
                                          List *restrictinfo_list)
 {
        List       *i;
@@ -272,7 +291,7 @@ match_index_orclauses(RelOptInfo *rel,
                         */
                        restrictinfo->subclauseindices =
                                match_index_orclause(rel, index,
-                                                                        indexkey, xclass,
+                                                                        indexkey, opclass,
                                                                         restrictinfo->clause->args,
                                                                         restrictinfo->subclauseindices);
                }
@@ -302,9 +321,9 @@ match_index_orclauses(RelOptInfo *rel,
  */
 static List *
 match_index_orclause(RelOptInfo *rel,
-                                        RelOptInfo *index,
+                                        IndexOptInfo *index,
                                         int indexkey,
-                                        int xclass,
+                                        Oid opclass,
                                         List *or_clauses,
                                         List *other_matching_indices)
 {
@@ -330,7 +349,7 @@ match_index_orclause(RelOptInfo *rel,
        {
                Expr       *clause = lfirst(clist);
 
-               if (match_or_subclause_to_indexkey(rel, index, indexkey, xclass,
+               if (match_or_subclause_to_indexkey(rel, index, indexkey, opclass,
                                                                                   clause))
                {
                        /* OK to add this index to sublist for this subclause */
@@ -353,9 +372,9 @@ match_index_orclause(RelOptInfo *rel,
  */
 static bool
 match_or_subclause_to_indexkey(RelOptInfo *rel,
-                                                          RelOptInfo *index,
+                                                          IndexOptInfo *index,
                                                           int indexkey,
-                                                          int xclass,
+                                                          Oid opclass,
                                                           Expr *clause)
 {
        if (and_clause((Node *) clause))
@@ -364,14 +383,14 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
 
                foreach(item, clause->args)
                {
-                       if (! match_clause_to_indexkey(rel, index, indexkey, xclass,
+                       if (! match_clause_to_indexkey(rel, index, indexkey, opclass,
                                                                                   lfirst(item), false))
                                return false;
                }
                return true;
        }
        else
-               return match_clause_to_indexkey(rel, index, indexkey, xclass,
+               return match_clause_to_indexkey(rel, index, indexkey, opclass,
                                                                                clause, false);
 }
 
@@ -420,7 +439,7 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
  */
 static List *
 group_clauses_by_indexkey(RelOptInfo *rel,
-                                                 RelOptInfo *index,
+                                                 IndexOptInfo *index,
                                                  int *indexkeys,
                                                  Oid *classes,
                                                  List *restrictinfo_list)
@@ -482,7 +501,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
  */
 static List *
 group_clauses_by_ikey_for_joins(RelOptInfo *rel,
-                                                               RelOptInfo *index,
+                                                               IndexOptInfo *index,
                                                                int *indexkeys,
                                                                Oid *classes,
                                                                List *join_cinfo_list,
@@ -588,7 +607,7 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
  * 'rel' is the relation of interest.
  * 'index' is an index on 'rel'.
  * 'indexkey' is a key of 'index'.
- * 'xclass' is the corresponding operator class.
+ * 'opclass' is the corresponding operator class.
  * 'clause' is the clause to be tested.
  * 'join' is true if we are considering this clause for joins.
  *
@@ -599,9 +618,9 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
  */
 static bool
 match_clause_to_indexkey(RelOptInfo *rel,
-                                                RelOptInfo *index,
+                                                IndexOptInfo *index,
                                                 int indexkey,
-                                                int xclass,
+                                                Oid opclass,
                                                 Expr *clause,
                                                 bool join)
 {
@@ -627,26 +646,28 @@ match_clause_to_indexkey(RelOptInfo *rel,
                if ((IsA(rightop, Const) || IsA(rightop, Param)) &&
                        match_index_to_operand(indexkey, leftop, rel, index))
                {
-                       if (indexable_operator(clause, xclass, index->relam, true))
+                       if (is_indexable_operator(clause, opclass, index->relam, true))
                                return true;
                        /*
                         * If we didn't find a member of the index's opclass,
                         * see whether it is a "special" indexable operator.
                         */
-                       if (match_special_index_operator(clause, true))
+                       if (match_special_index_operator(clause, opclass, index->relam,
+                                                                                        true))
                                return true;
                        return false;
                }
                if ((IsA(leftop, Const) || IsA(leftop, Param)) &&
                        match_index_to_operand(indexkey, rightop, rel, index))
                {
-                       if (indexable_operator(clause, xclass, index->relam, false))
+                       if (is_indexable_operator(clause, opclass, index->relam, false))
                                return true;
                        /*
                         * If we didn't find a member of the index's opclass,
                         * see whether it is a "special" indexable operator.
                         */
-                       if (match_special_index_operator(clause, false))
+                       if (match_special_index_operator(clause, opclass, index->relam,
+                                                                                        false))
                                return true;
                        return false;
                }
@@ -666,7 +687,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
                        isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
                        freeList(othervarnos);
                        if (isIndexable &&
-                               indexable_operator(clause, xclass, index->relam, true))
+                               is_indexable_operator(clause, opclass, index->relam, true))
                                return true;
                }
                else if (match_index_to_operand(indexkey, rightop, rel, index))
@@ -677,7 +698,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
                        isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
                        freeList(othervarnos);
                        if (isIndexable &&
-                               indexable_operator(clause, xclass, index->relam, false))
+                               is_indexable_operator(clause, opclass, index->relam, false))
                                return true;
                }
        }
@@ -690,9 +711,9 @@ match_clause_to_indexkey(RelOptInfo *rel,
  *       Does a binary opclause contain an operator matching the index's
  *       access method?
  *
- *       If the indexkey is on the right, what we actually want to know
- *       is whether the operator has a commutator operator that matches
- *       the index's access method.
+ * If the indexkey is on the right, what we actually want to know
+ * is whether the operator has a commutator operator that matches
+ * the index's access method.
  *
  * We try both the straightforward match and matches that rely on
  * recognizing binary-compatible datatypes.  For example, if we have
@@ -700,13 +721,14 @@ match_clause_to_indexkey(RelOptInfo *rel,
  * which we need to replace with oideq in order to recognize it as
  * matching an oid_ops index on the oid field.
  *
- * NOTE: if a binary-compatible match is made, we destructively modify
- * the given clause to use the binary-compatible substitute operator!
- * This should be safe even if we don't end up using the index, but it's
- * a tad ugly...
+ * Returns the OID of the matching operator, or InvalidOid if no match.
+ * Note that the returned OID will be different from the one in the given
+ * expression if we used a binary-compatible substitution.  Also note that
+ * if indexkey_on_left is FALSE (meaning we need to commute), the returned
+ * OID is *not* commuted; it can be plugged directly into the given clause.
  */
-static bool
-indexable_operator(Expr *clause, int xclass, Oid relam,
+Oid
+indexable_operator(Expr *clause, Oid opclass, Oid relam,
                                   bool indexkey_on_left)
 {
        Oid                     expr_op = ((Oper *) clause->oper)->opno;
@@ -720,11 +742,11 @@ indexable_operator(Expr *clause, int xclass, Oid relam,
        else
                commuted_op = get_commutator(expr_op);
        if (commuted_op == InvalidOid)
-               return false;
+               return InvalidOid;
 
        /* Done if the (commuted) operator is a member of the index's AM */
-       if (op_class(commuted_op, xclass, relam))
-               return true;
+       if (op_class(commuted_op, opclass, relam))
+               return expr_op;
 
        /*
         * Maybe the index uses a binary-compatible operator set.
@@ -741,7 +763,7 @@ indexable_operator(Expr *clause, int xclass, Oid relam,
                Operator        newop;
 
                if (opname == NULL)
-                       return false;           /* probably shouldn't happen */
+                       return InvalidOid;      /* probably shouldn't happen */
 
                /* Use the datatype of the index key */
                if (indexkey_on_left)
@@ -764,22 +786,15 @@ indexable_operator(Expr *clause, int xclass, Oid relam,
                                else
                                        commuted_op = get_commutator(new_expr_op);
                                if (commuted_op == InvalidOid)
-                                       return false;
+                                       return InvalidOid;
 
-                               if (op_class(commuted_op, xclass, relam))
-                               {
-                                       /*
-                                        * Success!  Change the opclause to use the
-                                        * binary-compatible operator.
-                                        */
-                                       ((Oper *) clause->oper)->opno = new_expr_op;
-                                       return true;
-                               }
+                               if (op_class(commuted_op, opclass, relam))
+                                       return new_expr_op;
                        }
                }
        }
 
-       return false;
+       return InvalidOid;
 }
 
 /*
@@ -799,7 +814,7 @@ indexable_operator(Expr *clause, int xclass, Oid relam,
  */
 static bool
 useful_for_mergejoin(RelOptInfo *rel,
-                                        RelOptInfo *index,
+                                        IndexOptInfo *index,
                                         List *joininfo_list)
 {
        int                *indexkeys = index->indexkeys;
@@ -850,7 +865,7 @@ useful_for_mergejoin(RelOptInfo *rel,
 static bool
 useful_for_ordering(Query *root,
                                        RelOptInfo *rel,
-                                       RelOptInfo *index)
+                                       IndexOptInfo *index)
 {
        List       *index_pathkeys;
 
@@ -1318,7 +1333,7 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
  * '*outerrelids' receives a list of relid lists
  */
 static void
-indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
+indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
                                          List *joininfo_list, List *restrictinfo_list,
                                          List **clausegroups, List **outerrelids)
 {
@@ -1367,7 +1382,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
  * Returns a list of index pathnodes.
  */
 static List *
-index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
+index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                                List *clausegroup_list, List *outerrelids_list)
 {
        List       *path_list = NIL;
@@ -1378,19 +1393,6 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
                List       *clausegroup = lfirst(i);
                IndexPath  *pathnode = makeNode(IndexPath);
                List       *indexquals;
-               float           npages;
-               float           selec;
-
-               indexquals = get_actual_clauses(clausegroup);
-               /* expand special operators to indexquals the executor can handle */
-               indexquals = expand_indexqual_conditions(indexquals);
-
-               index_selectivity(root,
-                                                 lfirsti(rel->relids),
-                                                 lfirsti(index->relids),
-                                                 indexquals,
-                                                 &npages,
-                                                 &selec);
 
                /* XXX this code ought to be merged with create_index_path? */
 
@@ -1398,24 +1400,21 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
                pathnode->path.parent = rel;
                pathnode->path.pathkeys = build_index_pathkeys(root, rel, index);
 
+               indexquals = get_actual_clauses(clausegroup);
+               /* expand special operators to indexquals the executor can handle */
+               indexquals = expand_indexqual_conditions(indexquals);
+
                /* Note that we are making a pathnode for a single-scan indexscan;
                 * therefore, both indexid and indexqual should be single-element
                 * lists.
                 */
-               Assert(length(index->relids) == 1);
-               pathnode->indexid = index->relids;
+               pathnode->indexid = lconsi(index->indexoid, NIL);
                pathnode->indexqual = lcons(indexquals, NIL);
 
                /* joinrelids saves the rels needed on the outer side of the join */
                pathnode->joinrelids = lfirst(outerrelids_list);
 
-               pathnode->path.path_cost = cost_index((Oid) lfirsti(index->relids),
-                                                                                         (int) npages,
-                                                                                         selec,
-                                                                                         rel->pages,
-                                                                                         rel->tuples,
-                                                                                         index->pages,
-                                                                                         index->tuples,
+               pathnode->path.path_cost = cost_index(root, rel, index, indexquals,
                                                                                          true);
 
                path_list = lappend(path_list, pathnode);
@@ -1438,7 +1437,7 @@ static bool
 match_index_to_operand(int indexkey,
                                           Var *operand,
                                           RelOptInfo *rel,
-                                          RelOptInfo *index)
+                                          IndexOptInfo *index)
 {
        if (index->indproc == InvalidOid)
        {
@@ -1460,7 +1459,7 @@ match_index_to_operand(int indexkey,
 }
 
 static bool
-function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
+function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
 {
        int                     relvarno = lfirsti(rel->relids);
        Func       *function;
@@ -1561,7 +1560,8 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
  * Return 'true' if we can do something with it anyway.
  */
 static bool
-match_special_index_operator(Expr *clause, bool indexkey_on_left)
+match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
+                                                        bool indexkey_on_left)
 {
        bool            isIndexable = false;
        Var                *leftop,
@@ -1625,6 +1625,51 @@ match_special_index_operator(Expr *clause, bool indexkey_on_left)
                        break;
        }
 
+       /* done if the expression doesn't look indexable */
+       if (! isIndexable)
+               return false;
+
+       /*
+        * Must also check that index's opclass supports the operators we will
+        * want to apply.  (A hash index, for example, will not support ">=".)
+        * We cheat a little by not checking for availability of "=" ... any
+        * index type should support "=", methinks.
+        */
+       switch (expr_op)
+       {
+               case OID_TEXT_LIKE_OP:
+               case OID_TEXT_REGEXEQ_OP:
+               case OID_TEXT_ICREGEXEQ_OP:
+                       if (! op_class(find_operator(">=", TEXTOID), opclass, relam) ||
+                               ! op_class(find_operator("<", TEXTOID), opclass, relam))
+                               isIndexable = false;
+                       break;
+
+               case OID_BPCHAR_LIKE_OP:
+               case OID_BPCHAR_REGEXEQ_OP:
+               case OID_BPCHAR_ICREGEXEQ_OP:
+                       if (! op_class(find_operator(">=", BPCHAROID), opclass, relam) ||
+                               ! op_class(find_operator("<", BPCHAROID), opclass, relam))
+                               isIndexable = false;
+                       break;
+
+               case OID_VARCHAR_LIKE_OP:
+               case OID_VARCHAR_REGEXEQ_OP:
+               case OID_VARCHAR_ICREGEXEQ_OP:
+                       if (! op_class(find_operator(">=", VARCHAROID), opclass, relam) ||
+                               ! op_class(find_operator("<", VARCHAROID), opclass, relam))
+                               isIndexable = false;
+                       break;
+
+               case OID_NAME_LIKE_OP:
+               case OID_NAME_REGEXEQ_OP:
+               case OID_NAME_ICREGEXEQ_OP:
+                       if (! op_class(find_operator(">=", NAMEOID), opclass, relam) ||
+                               ! op_class(find_operator("<", NAMEOID), opclass, relam))
+                               isIndexable = false;
+                       break;
+       }
+
        return isIndexable;
 }
 
@@ -1717,7 +1762,7 @@ expand_indexqual_conditions(List *indexquals)
 
 /*
  * Extract the fixed prefix, if any, for a LIKE pattern.
- * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * *prefix is set to a palloc'd prefix string,
  * or to NULL if no fixed prefix exists for the pattern.
  * The return value distinguishes no fixed prefix, a partial prefix,
  * or an exact-match-only pattern.
@@ -1729,7 +1774,7 @@ like_fixed_prefix(char *patt, char **prefix)
        int                     pos,
                                match_pos;
 
-       *prefix = match = palloc(strlen(patt)+2);
+       *prefix = match = palloc(strlen(patt)+1);
        match_pos = 0;
 
        for (pos = 0; patt[pos]; pos++)
@@ -1766,7 +1811,7 @@ like_fixed_prefix(char *patt, char **prefix)
 
 /*
  * Extract the fixed prefix, if any, for a regex pattern.
- * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * *prefix is set to a palloc'd prefix string,
  * or to NULL if no fixed prefix exists for the pattern.
  * The return value distinguishes no fixed prefix, a partial prefix,
  * or an exact-match-only pattern.
@@ -1801,7 +1846,7 @@ regex_fixed_prefix(char *patt, bool case_insensitive,
        }
 
        /* OK, allocate space for pattern */
-       *prefix = match = palloc(strlen(patt)+2);
+       *prefix = match = palloc(strlen(patt)+1);
        match_pos = 0;
 
        /* note start at pos 1 to skip leading ^ */
@@ -1848,12 +1893,11 @@ prefix_quals(Var *leftop, Oid expr_op,
 {
        List       *result;
        Oid                     datatype;
-       HeapTuple       optup;
-       void       *conval;
+       Oid                     oproid;
        Const      *con;
        Oper       *op;
        Expr       *expr;
-       int                     prefixlen;
+       char       *greaterstr;
 
        Assert(pstatus != Prefix_None);
 
@@ -1893,22 +1937,11 @@ prefix_quals(Var *leftop, Oid expr_op,
         */
        if (pstatus == Prefix_Exact)
        {
-               optup = SearchSysCacheTuple(OPERNAME,
-                                                                       PointerGetDatum("="),
-                                                                       ObjectIdGetDatum(datatype),
-                                                                       ObjectIdGetDatum(datatype),
-                                                                       CharGetDatum('b'));
-               if (!HeapTupleIsValid(optup))
+               oproid = find_operator("=", datatype);
+               if (oproid == InvalidOid)
                        elog(ERROR, "prefix_quals: no = operator for type %u", datatype);
-               /* Note: we cheat a little by assuming that textin() will do for
-                * bpchar and varchar constants too...
-                */
-               conval = (datatype == NAMEOID) ?
-                       (void*) namein(prefix) : (void*) textin(prefix);
-               con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
-                                               PointerGetDatum(conval),
-                                               false, false, false, false);
-               op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+               con = string_to_const(prefix, datatype);
+               op = makeOper(oproid, InvalidOid, BOOLOID, 0, NULL);
                expr = make_opclause(op, leftop, (Var *) con);
                result = lcons(expr, NIL);
                return result;
@@ -1919,50 +1952,178 @@ prefix_quals(Var *leftop, Oid expr_op,
         *
         * We can always say "x >= prefix".
         */
-       optup = SearchSysCacheTuple(OPERNAME,
-                                                               PointerGetDatum(">="),
-                                                               ObjectIdGetDatum(datatype),
-                                                               ObjectIdGetDatum(datatype),
-                                                               CharGetDatum('b'));
-       if (!HeapTupleIsValid(optup))
+       oproid = find_operator(">=", datatype);
+       if (oproid == InvalidOid)
                elog(ERROR, "prefix_quals: no >= operator for type %u", datatype);
-       conval = (datatype == NAMEOID) ?
-               (void*) namein(prefix) : (void*) textin(prefix);
-       con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
-                                       PointerGetDatum(conval),
-                                       false, false, false, false);
-       op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+       con = string_to_const(prefix, datatype);
+       op = makeOper(oproid, InvalidOid, BOOLOID, 0, NULL);
        expr = make_opclause(op, leftop, (Var *) con);
        result = lcons(expr, NIL);
 
        /*
-        * In ASCII locale we say "x <= prefix\377".  This does not
-        * work for non-ASCII collation orders, and it's not really
-        * right even for ASCII.  FIX ME!
-        * Note we assume the passed prefix string is workspace with
-        * an extra byte, as created by the xxx_fixed_prefix routines above.
+        * If we can create a string larger than the prefix, say "x < greaterstr".
         */
-#ifndef USE_LOCALE
-       prefixlen = strlen(prefix);
-       prefix[prefixlen] = '\377';
-       prefix[prefixlen+1] = '\0';
+       greaterstr = make_greater_string(prefix, datatype);
+       if (greaterstr)
+       {
+               oproid = find_operator("<", datatype);
+               if (oproid == InvalidOid)
+                       elog(ERROR, "prefix_quals: no < operator for type %u", datatype);
+               con = string_to_const(greaterstr, datatype);
+               op = makeOper(oproid, InvalidOid, BOOLOID, 0, NULL);
+               expr = make_opclause(op, leftop, (Var *) con);
+               result = lappend(result, expr);
+               pfree(greaterstr);
+       }
+
+       return result;
+}
+
+/*
+ * Try to generate a string greater than the given string or any string it is
+ * a prefix of.  If successful, return a palloc'd string; else return NULL.
+ *
+ * To work correctly in non-ASCII locales with weird collation orders,
+ * we cannot simply increment "foo" to "fop" --- we have to check whether
+ * we actually produced a string greater than the given one.  If not,
+ * increment the righthand byte again and repeat.  If we max out the righthand
+ * byte, truncate off the last character and start incrementing the next.
+ * For example, if "z" were the last character in the sort order, then we
+ * could produce "foo" as a string greater than "fonz".
+ *
+ * This could be rather slow in the worst case, but in most cases we won't
+ * have to try more than one or two strings before succeeding.
+ *
+ * XXX in a sufficiently weird locale, this might produce incorrect results?
+ * For example, in German I believe "ss" is treated specially --- if we are
+ * given "foos" and return "foot", will this actually be greater than "fooss"?
+ */
+static char *
+make_greater_string(const char * str, Oid datatype)
+{
+       char       *workstr;
+       int                     len;
+
+       /* Make a modifiable copy, which will be our return value if successful */
+       workstr = pstrdup((char *) str);
+
+       while ((len = strlen(workstr)) > 0)
+       {
+               unsigned char  *lastchar = (unsigned char *) (workstr + len - 1);
+
+               /*
+                * Try to generate a larger string by incrementing the last byte.
+                */
+               while (*lastchar < (unsigned char) 255)
+               {
+                       (*lastchar)++;
+                       if (string_lessthan(str, workstr, datatype))
+                               return workstr;                 /* Success! */
+               }
+               /*
+                * Truncate off the last character, which might be more than 1 byte
+                * in MULTIBYTE case.
+                */
+#ifdef MULTIBYTE
+               len = pg_mbcliplen((const unsigned char *) workstr, len, len-1);
+               workstr[len] = '\0';
+#else
+               *lastchar = '\0';
+#endif
+       }
+
+       /* Failed... */
+       pfree(workstr);
+       return NULL;
+}
+
+/*
+ * Handy subroutines for match_special_index_operator() and friends.
+ */
+
+/* See if there is a binary op of the given name for the given datatype */
+static Oid
+find_operator(const char * opname, Oid datatype)
+{
+       HeapTuple       optup;
 
        optup = SearchSysCacheTuple(OPERNAME,
-                                                               PointerGetDatum("<="),
+                                                               PointerGetDatum(opname),
                                                                ObjectIdGetDatum(datatype),
                                                                ObjectIdGetDatum(datatype),
                                                                CharGetDatum('b'));
        if (!HeapTupleIsValid(optup))
-               elog(ERROR, "prefix_quals: no <= operator for type %u", datatype);
-       conval = (datatype == NAMEOID) ?
-               (void*) namein(prefix) : (void*) textin(prefix);
-       con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
-                                       PointerGetDatum(conval),
-                                       false, false, false, false);
-       op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
-       expr = make_opclause(op, leftop, (Var *) con);
-       result = lappend(result, expr);
-#endif
+               return InvalidOid;
+       return optup->t_data->t_oid;
+}
+
+/*
+ * Generate a Datum of the appropriate type from a C string.
+ * Note that all of the supported types are pass-by-ref, so the
+ * returned value should be pfree'd if no longer needed.
+ */
+static Datum
+string_to_datum(const char * str, Oid datatype)
+{
+       /* We cheat a little by assuming that textin() will do for
+        * bpchar and varchar constants too...
+        */
+       if (datatype == NAMEOID)
+               return PointerGetDatum(namein((char *) str));
+       else
+               return PointerGetDatum(textin((char *) str));
+}
+
+/*
+ * Generate a Const node of the appropriate type from a C string.
+ */
+static Const *
+string_to_const(const char * str, Oid datatype)
+{
+       Datum           conval = string_to_datum(str, datatype);
+
+       return makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+                                        conval, false, false, false, false);
+}
+
+/*
+ * Test whether two strings are "<" according to the rules of the given
+ * datatype.  We do this the hard way, ie, actually calling the type's
+ * "<" operator function, to ensure we get the right result...
+ */
+static bool
+string_lessthan(const char * str1, const char * str2, Oid datatype)
+{
+       Datum           datum1 = string_to_datum(str1, datatype);
+       Datum           datum2 = string_to_datum(str2, datatype);
+       bool            result;
+
+       switch (datatype)
+       {
+               case TEXTOID:
+                       result = text_lt((text *) datum1, (text *) datum2);
+                       break;
+
+               case BPCHAROID:
+                       result = bpcharlt((char *) datum1, (char *) datum2);
+                       break;
+
+               case VARCHAROID:
+                       result = varcharlt((char *) datum1, (char *) datum2);
+                       break;
+
+               case NAMEOID:
+                       result = namelt((NameData *) datum1, (NameData *) datum2);
+                       break;
+
+               default:
+                       elog(ERROR, "string_lessthan: unexpected datatype %u", datatype);
+                       result = false;
+                       break;
+       }
+
+       pfree(DatumGetPointer(datum1));
+       pfree(DatumGetPointer(datum2));
 
        return result;
 }