*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
* 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
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)
{
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)
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;
}
}
/* Failed... */
+ if (cmptxt)
+ pfree(cmptxt);
pfree(workstr);
return NULL;