diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 095b389eb127f1de80b47bdc2226863f870e2e0c..9ae33ac456d6aebbcda1a0de099e16ab0fb96d06 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.238 2007/11/07 22:37:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.239 2007/11/09 20:10:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4586,7 +4586,10 @@ pattern_selectivity(Const *patt, Pattern_Type ptype) /* * 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 - * in the form of a Const pointer; else return NULL. + * in the form of a Const node; else return NULL. + * + * The caller must provide the appropriate "less than" comparison function + * for testing the strings. * * The key requirement here is that given a prefix string, say "foo", * we must be able to generate another string "fop" that is greater than @@ -4595,11 +4598,13 @@ pattern_selectivity(Const *patt, Pattern_Type ptype) * that is not a bulletproof guarantee that an extension of the string might * not sort after it; an example is that "foo " is less than "foo!", but it * is not clear that a "dictionary" sort ordering will consider "foo!" less - * than "foo bar". Therefore, this function should be used only for + * than "foo bar". CAUTION: Therefore, this function should be used only for * estimation purposes when working in a non-C locale. * - * The caller must provide the appropriate "less than" comparison function - * for testing the strings. + * To try to catch most cases where an extended string might otherwise sort + * before the result value, we determine which of the strings "Z", "z", "y", + * and "9" is seen as largest by the locale, and append that to the given + * prefix before trying to find a string that compares as larger. * * If we max out the righthand byte, truncate off the last character * and start incrementing the next. For example, if "z" were the last @@ -4615,13 +4620,22 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) Oid datatype = str_const->consttype; char *workstr; int len; + Datum cmpstr; + text *cmptxt = NULL; - /* Get a modifiable copy of the string in C-string format */ + /* + * Get a modifiable copy of the prefix string in C-string format, + * and set up the string we will compare to as a Datum. In C locale + * this can just be the given prefix string, otherwise we need to add + * a suffix. Types NAME and BYTEA sort bytewise so they don't need + * a suffix either. + */ if (datatype == NAMEOID) { workstr = DatumGetCString(DirectFunctionCall1(nameout, str_const->constvalue)); len = strlen(workstr); + cmpstr = str_const->constvalue; } else if (datatype == BYTEAOID) { @@ -4632,12 +4646,41 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) memcpy(workstr, VARDATA(bstr), len); if ((Pointer) bstr != DatumGetPointer(str_const->constvalue)) pfree(bstr); + cmpstr = str_const->constvalue; } else { workstr = DatumGetCString(DirectFunctionCall1(textout, str_const->constvalue)); len = strlen(workstr); + if (lc_collate_is_c() || len == 0) + cmpstr = str_const->constvalue; + else + { + /* If first time through, determine the suffix to use */ + static char suffixchar = 0; + + if (!suffixchar) + { + char *best; + + best = "Z"; + if (varstr_cmp(best, 1, "z", 1) < 0) + best = "z"; + if (varstr_cmp(best, 1, "y", 1) < 0) + best = "y"; + if (varstr_cmp(best, 1, "9", 1) < 0) + best = "9"; + suffixchar = *best; + } + + /* And build the string to compare to */ + cmptxt = (text *) palloc(VARHDRSZ + len + 1); + SET_VARSIZE(cmptxt, VARHDRSZ + len + 1); + memcpy(VARDATA(cmptxt), workstr, len); + *(VARDATA(cmptxt) + len) = suffixchar; + cmpstr = PointerGetDatum(cmptxt); + } } while (len > 0) @@ -4665,10 +4708,12 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) workstr_const = string_to_bytea_const(workstr, len); if (DatumGetBool(FunctionCall2(ltproc, - str_const->constvalue, + cmpstr, workstr_const->constvalue))) { - /* Successfully made a string larger than the input */ + /* Successfully made a string larger than cmpstr */ + if (cmptxt) + pfree(cmptxt); pfree(workstr); return workstr_const; } @@ -4695,6 +4740,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) } /* Failed... */ + if (cmptxt) + pfree(cmptxt); pfree(workstr); return NULL;