static void
btsortsupport_worker(SortSupport ssup, Oid collid)
{
- TextSortSupport *tss;
bool abbreviate = ssup->abbreviate;
+ bool locale_aware = false;
+ TextSortSupport *tss;
- /*
- * WIN32 requires complex hacks when the database encoding is UTF-8 (except
- * when using the "C" collation). For now, we don't optimize that case.
- * The use of abbreviated keys is also disabled on Windows, because
- * strxfrm() doesn't appear to work properly on some Windows systems.
- * Ideally, we would use it on those systems where it's reliable and
- * skip it only for the rest, but at the moment we don't know how to
- * distinguish between the ones where it works and the ones where it
- * doesn't.
- */
-#ifdef WIN32
- if (GetDatabaseEncoding() == PG_UTF8 && !lc_collate_is_c(collid))
- return;
- abbreviate = false;
+#ifdef HAVE_LOCALE_T
+ pg_locale_t locale = 0;
#endif
/*
- * On platforms where the abbreviated key for text optimization might have
- * bad worst case performance, it may be useful to avoid it entirely by
- * disabling it at compile time. Having only 4 byte datums could make
- * worst-case performance drastically more likely, for example. Moreover,
- * Darwin's strxfrm() implementations is known to not effectively
- * concentrate a significant amount of entropy from the original string in
- * earlier transformed blobs. It's possible that other supported platforms
- * are similarly encumbered.
- *
- * Any reasonable implementation will pack primary weights into the start
- * of returned blobs. The canonical algorithm's implementation is
- * discussed by Unicode Technical Standard #10 ("UNICODE COLLATION
- * ALGORITHM"), section 4, "Main algorithm". Section 4.3, "Form Sort Key"
- * is of particular interest:
- *
- * http://www.unicode.org/reports/tr10/#Step_3
- *
- * The collation algorithm standard goes on to state:
- *
- * "By default, the algorithm makes use of three fully-customizable levels.
- * For the Latin script, these levels correspond roughly to:
- *
- * alphabetic ordering
- *
- * diacritic ordering
- *
- * case ordering.
+ * If possible, set ssup->comparator to a function which can be used to
+ * directly compare two datums. If we can do this, we'll avoid the
+ * overhead of a trip through the fmgr layer for every comparison,
+ * which can be substantial.
*
- * A final level may be used for tie-breaking between strings not otherwise
- * distinguished."
+ * Most typically, we'll set the comparator to bttextfastcmp_locale,
+ * which uses strcoll() to perform comparisons. However, if LC_COLLATE
+ * = C, we can make things quite a bit faster with bttextfastcmp_c,
+ * which uses memcmp() rather than strcoll().
*
- * It is generally expected that most non-equal keys will have their
- * comparisons resolved at the primary level. If enough comparisons can be
- * resolved with just 4 or 8 byte abbreviated keys, this optimization is
- * very effective (although if there are many tie-breakers that largely
- * only perform cheap memcmp() calls, that is also much faster than the
- * unoptimized case - see bttext_abbrev_abort()).
- *
- * We may need a collation-sensitive comparison. To make things faster,
- * we'll figure out the collation based on the locale id and cache the
- * result. Also, since strxfrm()/strcoll() require NUL-terminated inputs,
- * prepare one or two palloc'd buffers to use as temporary workspace. In
- * the ad-hoc comparison case we only use palloc'd buffers when we need
- * more space than we're comfortable allocating on the stack, but here we
- * can keep the buffers around for the whole sort, so it makes sense to
- * allocate them once and use them unconditionally.
+ * There is a further exception on Windows. When the database encoding
+ * is UTF-8 and we are not using the C collation, complex hacks are
+ * required. We don't currently have a comparator that handles that case,
+ * so we fall back on the slow method of having the sort code invoke
+ * bttextcmp() via the fmgr trampoline.
*/
- tss = palloc(sizeof(TextSortSupport));
-#ifdef HAVE_LOCALE_T
- tss->locale = 0;
+ if (lc_collate_is_c(collid))
+ ssup->comparator = bttextfastcmp_c;
+#ifdef WIN32
+ else if (GetDatabaseEncoding() == PG_UTF8)
+ return;
#endif
-
- if (collid != DEFAULT_COLLATION_OID)
+ else
{
- if (!OidIsValid(collid))
+ ssup->comparator = bttextfastcmp_locale;
+ locale_aware = true;
+
+ /*
+ * We need a collation-sensitive comparison. To make things faster,
+ * we'll figure out the collation based on the locale id and cache the
+ * result.
+ */
+ if (collid != DEFAULT_COLLATION_OID)
{
- /*
- * This typically means that the parser could not resolve a
- * conflict of implicit collations, so report it that way.
- */
- ereport(ERROR,
- (errcode(ERRCODE_INDETERMINATE_COLLATION),
- errmsg("could not determine which collation to use for string comparison"),
- errhint("Use the COLLATE clause to set the collation explicitly.")));
- }
+ if (!OidIsValid(collid))
+ {
+ /*
+ * This typically means that the parser could not resolve a
+ * conflict of implicit collations, so report it that way.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_COLLATION),
+ errmsg("could not determine which collation to use for string comparison"),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
+ }
#ifdef HAVE_LOCALE_T
- tss->locale = pg_newlocale_from_collation(collid);
+ tss->locale = pg_newlocale_from_collation(collid);
#endif
+ }
}
/*
- * If LC_COLLATE = C, we can make things quite a bit faster by using
- * memcmp() rather than strcoll(). To minimize the per-comparison
- * overhead, we make this decision just once for the whole sort.
+ * It's possible that there are platforms where the use of abbreviated
+ * keys should be disabled at compile time. Having only 4 byte datums
+ * could make worst-case performance drastically more likely, for example.
+ * Moreover, Darwin's strxfrm() implementations is known to not effectively
+ * concentrate a significant amount of entropy from the original string in
+ * earlier transformed blobs. It's possible that other supported platforms
+ * are similarly encumbered. However, even in those cases, the abbreviated
+ * keys optimization may win, and if it doesn't, the "abort abbreviation"
+ * code may rescue us. So, for now, we don't disable this anywhere on the
+ * basis of performance.
*
- * There is no reason to not at least perform fmgr elision on builds where
- * abbreviation is disabled.
+ * On Windows, however, strxfrm() seems to be unreliable on some machines,
+ * so we categorically disable this optimization there.
*/
- if (lc_collate_is_c(collid))
- ssup->abbrev_full_comparator = ssup->comparator = bttextfastcmp_c;
- else
- ssup->abbrev_full_comparator = ssup->comparator = bttextfastcmp_locale;
+#ifdef WIN32
+ abbreviate = false;
+#endif
- if (!lc_collate_is_c(collid) || abbreviate)
+ /*
+ * If we're using abbreviated keys, or if we're using a locale-aware
+ * comparison, we need to initialize a TextSortSupport object. Both cases
+ * will make use of the temporary buffers we initialize here for scratch
+ * space, and the abbreviation case requires additional state.
+ */
+ if (abbreviate || locale_aware)
{
- /*
- * Abbreviated case requires temp buffers for strxfrm() copying.
- * bttextfastcmp_locale() also uses these buffers (even if abbreviation
- * isn't used), while bttextfast_c() does not.
- */
+ tss = palloc(sizeof(TextSortSupport));
tss->buf1 = palloc(TEXTBUFLEN);
tss->buflen1 = TEXTBUFLEN;
tss->buf2 = palloc(TEXTBUFLEN);
tss->buflen2 = TEXTBUFLEN;
+#ifdef HAVE_LOCALE_T
+ tss->locale = locale;
+#endif
ssup->ssup_extra = tss;
- }
-
- if (!abbreviate)
- return;
- initHyperLogLog(&tss->abbr_card, 10);
- initHyperLogLog(&tss->full_card, 10);
-
- /*
- * Change comparator to be abbreviation-based -- abbreviated version will
- * probably ultimately be used during sorting proper, but core code may
- * switch back to authoritative comparator should abbreviation be aborted
- */
- ssup->comparator = bttextcmp_abbrev;
- ssup->abbrev_converter = bttext_abbrev_convert;
- ssup->abbrev_abort = bttext_abbrev_abort;
+ /*
+ * If possible, plan to use the abbreviated keys optimization. The
+ * core code may switch back to authoritative comparator should
+ * abbreviation be aborted.
+ */
+ if (abbreviate)
+ {
+ initHyperLogLog(&tss->abbr_card, 10);
+ initHyperLogLog(&tss->full_card, 10);
+ ssup->abbrev_full_comparator = ssup->comparator;
+ ssup->comparator = bttextcmp_abbrev;
+ ssup->abbrev_converter = bttext_abbrev_convert;
+ ssup->abbrev_abort = bttext_abbrev_abort;
+ }
+ }
}
/*