diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index dba650c34d530cb048ccf0cd6093f76124c91170..f26a77cf2936c413e87398614c3939b195d5313e 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -63,6 +63,7 @@ typedef struct char *buf2; /* 2nd string, or abbreviation strxfrm() buf */ int buflen1; int buflen2; + bool collate_c; hyperLogLogState abbr_card; /* Abbreviated key cardinality state */ hyperLogLogState full_card; /* Full key cardinality state */ #ifdef HAVE_LOCALE_T @@ -1744,7 +1745,7 @@ static void btsortsupport_worker(SortSupport ssup, Oid collid) { bool abbreviate = ssup->abbreviate; - bool locale_aware = false; + bool collate_c = false; TextSortSupport *tss; #ifdef HAVE_LOCALE_T @@ -1769,7 +1770,10 @@ btsortsupport_worker(SortSupport ssup, Oid collid) * bttextcmp() via the fmgr trampoline. */ if (lc_collate_is_c(collid)) + { ssup->comparator = bttextfastcmp_c; + collate_c = true; + } #ifdef WIN32 else if (GetDatabaseEncoding() == PG_UTF8) return; @@ -1777,7 +1781,6 @@ btsortsupport_worker(SortSupport ssup, Oid collid) else { ssup->comparator = bttextfastcmp_locale; - locale_aware = true; /* * We need a collation-sensitive comparison. To make things faster, @@ -1798,7 +1801,7 @@ btsortsupport_worker(SortSupport ssup, Oid collid) errhint("Use the COLLATE clause to set the collation explicitly."))); } #ifdef HAVE_LOCALE_T - tss->locale = pg_newlocale_from_collation(collid); + locale = pg_newlocale_from_collation(collid); #endif } } @@ -1828,7 +1831,7 @@ btsortsupport_worker(SortSupport ssup, Oid collid) * will make use of the temporary buffers we initialize here for scratch * space, and the abbreviation case requires additional state. */ - if (abbreviate || locale_aware) + if (abbreviate || !collate_c) { tss = palloc(sizeof(TextSortSupport)); tss->buf1 = palloc(TEXTBUFLEN); @@ -1838,6 +1841,7 @@ btsortsupport_worker(SortSupport ssup, Oid collid) #ifdef HAVE_LOCALE_T tss->locale = locale; #endif + tss->collate_c = collate_c; ssup->ssup_extra = tss; /* @@ -2011,45 +2015,58 @@ bttext_abbrev_convert(Datum original, SortSupport ssup) memset(pres, 0, sizeof(Datum)); len = VARSIZE_ANY_EXHDR(authoritative); - /* By convention, we use buffer 1 to store and NUL-terminate text */ - if (len >= tss->buflen1) + /* + * If we're using the C collation, use memcmp(), rather than strxfrm(), + * to abbreviated keys. The full comparator for the C locale is always + * memcmp(), and we can't risk having this give a different answer. + * Besides, this should be faster, too. + */ + if (tss->collate_c) + memcpy(pres, VARDATA_ANY(authoritative), Min(len, sizeof(Datum))); + else { - pfree(tss->buf1); - tss->buflen1 = Max(len + 1, Min(tss->buflen1 * 2, MaxAllocSize)); - tss->buf1 = palloc(tss->buflen1); - } + /* + * We're not using the C collation, so fall back on strxfrm. + */ - /* Just like strcoll(), strxfrm() expects a NUL-terminated string */ - memcpy(tss->buf1, VARDATA_ANY(authoritative), len); - tss->buf1[len] = '\0'; + /* By convention, we use buffer 1 to store and NUL-terminate text */ + if (len >= tss->buflen1) + { + pfree(tss->buf1); + tss->buflen1 = Max(len + 1, Min(tss->buflen1 * 2, MaxAllocSize)); + tss->buf1 = palloc(tss->buflen1); + } - /* Don't leak memory here */ - if (PointerGetDatum(authoritative) != original) - pfree(authoritative); + /* Just like strcoll(), strxfrm() expects a NUL-terminated string */ + memcpy(tss->buf1, VARDATA_ANY(authoritative), len); + tss->buf1[len] = '\0'; -retry: + /* Don't leak memory here */ + if (PointerGetDatum(authoritative) != original) + pfree(authoritative); - /* - * There is no special handling of the C locale here, unlike with - * varstr_cmp(). strxfrm() is used indifferently. - */ + for (;;) + { #ifdef HAVE_LOCALE_T - if (tss->locale) - bsize = strxfrm_l(tss->buf2, tss->buf1, tss->buflen2, tss->locale); - else + if (tss->locale) + bsize = strxfrm_l(tss->buf2, tss->buf1, + tss->buflen2, tss->locale); + else #endif - bsize = strxfrm(tss->buf2, tss->buf1, tss->buflen2); + bsize = strxfrm(tss->buf2, tss->buf1, tss->buflen2); - if (bsize >= tss->buflen2) - { - /* - * The C standard states that the contents of the buffer is now - * unspecified. Grow buffer, and retry. - */ - pfree(tss->buf2); - tss->buflen2 = Max(bsize + 1, Min(tss->buflen2 * 2, MaxAllocSize)); - tss->buf2 = palloc(tss->buflen2); - goto retry; + if (bsize < tss->buflen2) + break; + + /* + * The C standard states that the contents of the buffer is now + * unspecified. Grow buffer, and retry. + */ + pfree(tss->buf2); + tss->buflen2 = Max(bsize + 1, + Min(tss->buflen2 * 2, MaxAllocSize)); + tss->buf2 = palloc(tss->buflen2); + } } /*