diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index f35372ebbef..35c57434830 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.212.2.3 2007/07/31 19:53:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.212.2.4 2007/11/07 22:37:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2801,6 +2801,7 @@ prefix_quals(Node *leftop, Oid opclass, Oid datatype; Oid oproid; Expr *expr; + FmgrInfo ltproc; Const *greaterstr; Assert(pstatus != Pattern_Prefix_None); @@ -2897,13 +2898,14 @@ prefix_quals(Node *leftop, Oid opclass, * "x < greaterstr". *------- */ - greaterstr = make_greater_string(prefix_const); + oproid = get_opclass_member(opclass, InvalidOid, + BTLessStrategyNumber); + if (oproid == InvalidOid) + elog(ERROR, "no < operator for opclass %u", opclass); + fmgr_info(get_opcode(oproid), <proc); + greaterstr = make_greater_string(prefix_const, <proc); if (greaterstr) { - oproid = get_opclass_member(opclass, InvalidOid, - BTLessStrategyNumber); - if (oproid == InvalidOid) - elog(ERROR, "no < operator for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, (Expr *) leftop, (Expr *) greaterstr); result = lappend(result, diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index f1a83b0875b..661039a798a 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.214.2.7 2007/11/07 21:00:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.214.2.8 2007/11/07 22:37:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4286,17 +4286,17 @@ prefix_selectivity(VariableStatData *vardata, Oid opclass, Const *prefixcon) * "x < greaterstr". *------- */ - greaterstrcon = make_greater_string(prefixcon); + cmpopr = get_opclass_member(opclass, InvalidOid, + BTLessStrategyNumber); + if (cmpopr == InvalidOid) + elog(ERROR, "no < operator for opclass %u", opclass); + fmgr_info(get_opcode(cmpopr), &opproc); + + greaterstrcon = make_greater_string(prefixcon, &opproc); if (greaterstrcon) { Selectivity topsel; - cmpopr = get_opclass_member(opclass, InvalidOid, - BTLessStrategyNumber); - if (cmpopr == InvalidOid) - elog(ERROR, "no < operator for opclass %u", opclass); - fmgr_info(get_opcode(cmpopr), &opproc); - topsel = ineq_histogram_selectivity(vardata, &opproc, false, greaterstrcon->constvalue, greaterstrcon->consttype); @@ -4573,8 +4573,17 @@ pattern_selectivity(Const *patt, Pattern_Type ptype) * in the form of a Const pointer; else return NULL. * * 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 all strings "foobar" starting with "foo". + * we must be able to generate another string "fop" that is greater than + * all strings "foobar" starting with "foo". We can test that we have + * generated a string greater than the prefix string, but in non-C locales + * 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 + * estimation purposes when working in a non-C locale. + * + * The caller must provide the appropriate "less than" comparison function + * for testing the strings. * * If we max out the righthand byte, truncate off the last character * and start incrementing the next. For example, if "z" were the last @@ -4583,20 +4592,15 @@ pattern_selectivity(Const *patt, Pattern_Type ptype) * * 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. - * - * 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 UTF8, so we do have to watch out for generating - * invalid encoding sequences. */ Const * -make_greater_string(const Const *str_const) +make_greater_string(const Const *str_const, FmgrInfo *ltproc) { Oid datatype = str_const->consttype; char *workstr; int len; - /* Get the string and a modifiable copy */ + /* Get a modifiable copy of the string in C-string format */ if (datatype == NAMEOID) { workstr = DatumGetCString(DirectFunctionCall1(nameout, @@ -4644,8 +4648,18 @@ make_greater_string(const Const *str_const) else workstr_const = string_to_bytea_const(workstr, len); - pfree(workstr); - return workstr_const; + if (DatumGetBool(FunctionCall2(ltproc, + str_const->constvalue, + workstr_const->constvalue))) + { + /* Successfully made a string larger than the input */ + pfree(workstr); + return workstr_const; + } + + /* No good, release unusable value and try again */ + pfree(DatumGetPointer(workstr_const->constvalue)); + pfree(workstr_const); } /* restore last byte so we don't confuse pg_mbcliplen */ diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index 7976e67b2ee..95f71d3754d 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.36.2.1 2007/08/31 23:35:30 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.36.2.2 2007/11/07 22:37:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -118,7 +118,7 @@ extern Pattern_Prefix_Status pattern_fixed_prefix(Const *patt, Pattern_Type ptype, Const **prefix, Const **rest); -extern Const *make_greater_string(const Const *str_const); +extern Const *make_greater_string(const Const *str_const, FmgrInfo *ltproc); extern Datum eqsel(PG_FUNCTION_ARGS); extern Datum neqsel(PG_FUNCTION_ARGS);