Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/utils/adt/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 27 kB image not shown  

Quelle  pg_locale_icu.c

  Sprache: C
 

/*-----------------------------------------------------------------------
 *
 * PostgreSQL locale utilities for ICU
 *
 * Portions Copyright (c) 2002-2025, PostgreSQL Global Development Group
 *
 * src/backend/utils/adt/pg_locale_icu.c
 *
 *-----------------------------------------------------------------------
 */


#include "postgres.h"

#ifdef USE_ICU
#include <unicode/ucnv.h>
#include <unicode/ustring.h>

/*
 * ucol_strcollUTF8() was introduced in ICU 50, but it is buggy before ICU 53.
 * (see
 * <https://www.postgresql.org/message-id/flat/f1438ec6-22aa-4029-9a3b-26f79d330e72%40manitou-mail.org>)
 */

#if U_ICU_VERSION_MAJOR_NUM >= 53
#define HAVE_UCOL_STRCOLLUTF8 1
#else
#undef HAVE_UCOL_STRCOLLUTF8
#endif

#endif

#include "access/htup_details.h"
#include "catalog/pg_database.h"
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/syscache.h"

/*
 * Size of stack buffer to use for string transformations, used to avoid heap
 * allocations in typical cases. This should be large enough that most strings
 * will fit, but small enough that we feel comfortable putting it on the
 * stack.
 */

#define  TEXTBUFLEN   1024

extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
extern size_t strlower_icu(char *dest, size_t destsize, const char *src,
         ssize_t srclen, pg_locale_t locale);
extern size_t strtitle_icu(char *dest, size_t destsize, const char *src,
         ssize_t srclen, pg_locale_t locale);
extern size_t strupper_icu(char *dest, size_t destsize, const char *src,
         ssize_t srclen, pg_locale_t locale);
extern size_t strfold_icu(char *dest, size_t destsize, const char *src,
        ssize_t srclen, pg_locale_t locale);

#ifdef USE_ICU

extern UCollator *pg_ucol_open(const char *loc_str);

static size_t strnxfrm_icu(char *dest, size_t destsize,
         const char *src, ssize_t srclen,
         pg_locale_t locale);
extern char *get_collation_actual_version_icu(const char *collcollate);

typedef int32_t (*ICU_Convert_Func) (UChar *dest, int32_t destCapacity,
          const UChar *src, int32_t srcLength,
          const char *locale,
          UErrorCode *pErrorCode);

/*
 * Converter object for converting between ICU's UChar strings and C strings
 * in database encoding.  Since the database encoding doesn't change, we only
 * need one of these per session.
 */

static UConverter *icu_converter = NULL;

static UCollator *make_icu_collator(const char *iculocstr,
         const char *icurules);
static int strncoll_icu(const char *arg1, ssize_t len1,
       const char *arg2, ssize_t len2,
       pg_locale_t locale);
static size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
          const char *src, ssize_t srclen,
          pg_locale_t locale);
#ifdef HAVE_UCOL_STRCOLLUTF8
static int strncoll_icu_utf8(const char *arg1, ssize_t len1,
         const char *arg2, ssize_t len2,
         pg_locale_t locale);
#endif
static size_t strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
            const char *src, ssize_t srclen,
            pg_locale_t locale);
static void init_icu_converter(void);
static int32_t uchar_length(UConverter *converter,
       const char *str, int32_t len);
static int32_t uchar_convert(UConverter *converter,
        UChar *dest, int32_t destlen,
        const char *src, int32_t srclen);
static int32_t icu_to_uchar(UChar **buff_uchar, const char *buff,
       size_t nbytes);
static size_t icu_from_uchar(char *dest, size_t destsize,
        const UChar *buff_uchar, int32_t len_uchar);
static void icu_set_collation_attributes(UCollator *collator, const char *loc,
           UErrorCode *status);
static int32_t icu_convert_case(ICU_Convert_Func func, pg_locale_t mylocale,
        UChar **buff_dest, UChar *buff_source,
        int32_t len_source);
static int32_t u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
            const UChar *src, int32_t srcLength,
            const char *locale,
            UErrorCode *pErrorCode);
static int32_t u_strFoldCase_default(UChar *dest, int32_t destCapacity,
          const UChar *src, int32_t srcLength,
          const char *locale,
          UErrorCode *pErrorCode);

static const struct collate_methods collate_methods_icu = {
 .strncoll = strncoll_icu,
 .strnxfrm = strnxfrm_icu,
 .strnxfrm_prefix = strnxfrm_prefix_icu,
 .strxfrm_is_safe = true,
};

static const struct collate_methods collate_methods_icu_utf8 = {
#ifdef HAVE_UCOL_STRCOLLUTF8
 .strncoll = strncoll_icu_utf8,
#else
 .strncoll = strncoll_icu,
#endif
 .strnxfrm = strnxfrm_icu,
 .strnxfrm_prefix = strnxfrm_prefix_icu_utf8,
 .strxfrm_is_safe = true,
};

#endif

pg_locale_t
create_pg_locale_icu(Oid collid, MemoryContext context)
{
#ifdef USE_ICU
 bool  deterministic;
 const char *iculocstr;
 const char *icurules = NULL;
 UCollator  *collator;
 pg_locale_t result;

 if (collid == DEFAULT_COLLATION_OID)
 {
  HeapTuple tp;
  Datum  datum;
  bool  isnull;

  tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
  if (!HeapTupleIsValid(tp))
   elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);

  /* default database collation is always deterministic */
  deterministic = true;
  datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
            Anum_pg_database_datlocale);
  iculocstr = TextDatumGetCString(datum);
  datum = SysCacheGetAttr(DATABASEOID, tp,
        Anum_pg_database_daticurules, &isnull);
  if (!isnull)
   icurules = TextDatumGetCString(datum);

  ReleaseSysCache(tp);
 }
 else
 {
  Form_pg_collation collform;
  HeapTuple tp;
  Datum  datum;
  bool  isnull;

  tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
  if (!HeapTupleIsValid(tp))
   elog(ERROR, "cache lookup failed for collation %u", collid);
  collform = (Form_pg_collation) GETSTRUCT(tp);
  deterministic = collform->collisdeterministic;
  datum = SysCacheGetAttrNotNull(COLLOID, tp,
            Anum_pg_collation_colllocale);
  iculocstr = TextDatumGetCString(datum);
  datum = SysCacheGetAttr(COLLOID, tp,
        Anum_pg_collation_collicurules, &isnull);
  if (!isnull)
   icurules = TextDatumGetCString(datum);

  ReleaseSysCache(tp);
 }

 collator = make_icu_collator(iculocstr, icurules);

 result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
 result->info.icu.locale = MemoryContextStrdup(context, iculocstr);
 result->info.icu.ucol = collator;
 result->provider = COLLPROVIDER_ICU;
 result->deterministic = deterministic;
 result->collate_is_c = false;
 result->ctype_is_c = false;
 if (GetDatabaseEncoding() == PG_UTF8)
  result->collate = &collate_methods_icu_utf8;
 else
  result->collate = &collate_methods_icu;

 return result;
#else
 /* could get here if a collation was created by a build with ICU */
 ereport(ERROR,
   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    errmsg("ICU is not supported in this build")));

 return NULL;
#endif
}

#ifdef USE_ICU

/*
 * Wrapper around ucol_open() to handle API differences for older ICU
 * versions.
 *
 * Ensure that no path leaks a UCollator.
 */

UCollator *
pg_ucol_open(const char *loc_str)
{
 UCollator  *collator;
 UErrorCode status;
 const char *orig_str = loc_str;
 char    *fixed_str = NULL;

 /*
  * Must never open default collator, because it depends on the environment
  * and may change at any time. Should not happen, but check here to catch
  * bugs that might be hard to catch otherwise.
  *
  * NB: the default collator is not the same as the collator for the root
  * locale. The root locale may be specified as the empty string, "und", or
  * "root". The default collator is opened by passing NULL to ucol_open().
 */

 if (loc_str == NULL)
  elog(ERROR, "opening default collator is not supported");

 /*
  * In ICU versions 54 and earlier, "und" is not a recognized spelling of
  * the root locale. If the first component of the locale is "und", replace
  * with "root" before opening.
 */

 if (U_ICU_VERSION_MAJOR_NUM < 55)
 {
  char  lang[ULOC_LANG_CAPACITY];

  status = U_ZERO_ERROR;
  uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
  if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
  {
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("could not get language from locale \"%s\": %s",
       loc_str, u_errorName(status))));
  }

  if (strcmp(lang, "und") == 0)
  {
   const char *remainder = loc_str + strlen("und");

   fixed_str = palloc(strlen("root") + strlen(remainder) + 1);
   strcpy(fixed_str, "root");
   strcat(fixed_str, remainder);

   loc_str = fixed_str;
  }
 }

 status = U_ZERO_ERROR;
 collator = ucol_open(loc_str, &status);
 if (U_FAILURE(status))
  ereport(ERROR,
  /* use original string for error report */
    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     errmsg("could not open collator for locale \"%s\": %s",
      orig_str, u_errorName(status))));

 if (U_ICU_VERSION_MAJOR_NUM < 54)
 {
  status = U_ZERO_ERROR;
  icu_set_collation_attributes(collator, loc_str, &status);

  /*
   * Pretend the error came from ucol_open(), for consistent error
   * message across ICU versions.
 */

  if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
  {
   ucol_close(collator);
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("could not open collator for locale \"%s\": %s",
       orig_str, u_errorName(status))));
  }
 }

 if (fixed_str != NULL)
  pfree(fixed_str);

 return collator;
}

/*
 * Create a UCollator with the given locale string and rules.
 *
 * Ensure that no path leaks a UCollator.
 */

static UCollator *
make_icu_collator(const char *iculocstr, const char *icurules)
{
 if (!icurules)
 {
  /* simple case without rules */
  return pg_ucol_open(iculocstr);
 }
 else
 {
  UCollator  *collator_std_rules;
  UCollator  *collator_all_rules;
  const UChar *std_rules;
  UChar    *my_rules;
  UChar    *all_rules;
  int32_t  length;
  int32_t  total;
  UErrorCode status;

  /*
   * If rules are specified, we extract the rules of the standard
   * collation, add our own rules, and make a new collator with the
   * combined rules.
 */

  icu_to_uchar(&my_rules, icurules, strlen(icurules));

  collator_std_rules = pg_ucol_open(iculocstr);

  std_rules = ucol_getRules(collator_std_rules, &length);

  total = u_strlen(std_rules) + u_strlen(my_rules) + 1;

  /* avoid leaking collator on OOM */
  all_rules = palloc_array_extended(UChar, total, MCXT_ALLOC_NO_OOM);
  if (!all_rules)
  {
   ucol_close(collator_std_rules);
   ereport(ERROR,
     (errcode(ERRCODE_OUT_OF_MEMORY),
      errmsg("out of memory")));
  }

  u_strcpy(all_rules, std_rules);
  u_strcat(all_rules, my_rules);

  ucol_close(collator_std_rules);

  status = U_ZERO_ERROR;
  collator_all_rules = ucol_openRules(all_rules, u_strlen(all_rules),
           UCOL_DEFAULT, UCOL_DEFAULT_STRENGTH,
           NULL, &status);
  if (U_FAILURE(status))
  {
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("could not open collator for locale \"%s\" with rules \"%s\": %s",
       iculocstr, icurules, u_errorName(status))));
  }

  pfree(my_rules);
  pfree(all_rules);
  return collator_all_rules;
 }
}

size_t
strlower_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
    pg_locale_t locale)
{
 int32_t  len_uchar;
 int32_t  len_conv;
 UChar    *buff_uchar;
 UChar    *buff_conv;
 size_t  result_len;

 len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
 len_conv = icu_convert_case(u_strToLower, locale,
        &buff_conv, buff_uchar, len_uchar);
 result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
 pfree(buff_uchar);
 pfree(buff_conv);

 return result_len;
}

size_t
strtitle_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
    pg_locale_t locale)
{
 int32_t  len_uchar;
 int32_t  len_conv;
 UChar    *buff_uchar;
 UChar    *buff_conv;
 size_t  result_len;

 len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
 len_conv = icu_convert_case(u_strToTitle_default_BI, locale,
        &buff_conv, buff_uchar, len_uchar);
 result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
 pfree(buff_uchar);
 pfree(buff_conv);

 return result_len;
}

size_t
strupper_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
    pg_locale_t locale)
{
 int32_t  len_uchar;
 int32_t  len_conv;
 UChar    *buff_uchar;
 UChar    *buff_conv;
 size_t  result_len;

 len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
 len_conv = icu_convert_case(u_strToUpper, locale,
        &buff_conv, buff_uchar, len_uchar);
 result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
 pfree(buff_uchar);
 pfree(buff_conv);

 return result_len;
}

size_t
strfold_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
   pg_locale_t locale)
{
 int32_t  len_uchar;
 int32_t  len_conv;
 UChar    *buff_uchar;
 UChar    *buff_conv;
 size_t  result_len;

 len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
 len_conv = icu_convert_case(u_strFoldCase_default, locale,
        &buff_conv, buff_uchar, len_uchar);
 result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
 pfree(buff_uchar);
 pfree(buff_conv);

 return result_len;
}

/*
 * strncoll_icu_utf8
 *
 * Call ucol_strcollUTF8() or ucol_strcoll() as appropriate for the given
 * database encoding. An argument length of -1 means the string is
 * NUL-terminated.
 */

#ifdef HAVE_UCOL_STRCOLLUTF8
int
strncoll_icu_utf8(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2,
      pg_locale_t locale)
{
 int   result;
 UErrorCode status;

 Assert(locale->provider == COLLPROVIDER_ICU);

 Assert(GetDatabaseEncoding() == PG_UTF8);

 status = U_ZERO_ERROR;
 result = ucol_strcollUTF8(locale->info.icu.ucol,
         arg1, len1,
         arg2, len2,
         &status);
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("collation failed: %s", u_errorName(status))));

 return result;
}
#endif

/* 'srclen' of -1 means the strings are NUL-terminated */
size_t
strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
    pg_locale_t locale)
{
 UChar  sbuf[TEXTBUFLEN / sizeof(UChar)];
 UChar    *uchar = sbuf;
 int32_t  ulen;
 Size  result_bsize;

 Assert(locale->provider == COLLPROVIDER_ICU);

 init_icu_converter();

 ulen = uchar_length(icu_converter, src, srclen);

 if (ulen >= lengthof(sbuf))
  uchar = palloc_array(UChar, ulen + 1);

 ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);

 result_bsize = ucol_getSortKey(locale->info.icu.ucol,
           uchar, ulen,
           (uint8_t *) dest, destsize);

 /*
  * ucol_getSortKey() counts the nul-terminator in the result length, but
  * this function should not.
 */

 Assert(result_bsize > 0);
 result_bsize--;

 if (uchar != sbuf)
  pfree(uchar);

 /* if dest is defined, it should be nul-terminated */
 Assert(result_bsize >= destsize || dest[result_bsize] == '\0');

 return result_bsize;
}

/* 'srclen' of -1 means the strings are NUL-terminated */
size_t
strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
       const char *src, ssize_t srclen,
       pg_locale_t locale)
{
 size_t  result;
 UCharIterator iter;
 uint32_t state[2];
 UErrorCode status;

 Assert(locale->provider == COLLPROVIDER_ICU);

 Assert(GetDatabaseEncoding() == PG_UTF8);

 uiter_setUTF8(&iter, src, srclen);
 state[0] = state[1] = 0/* won't need that again */
 status = U_ZERO_ERROR;
 result = ucol_nextSortKeyPart(locale->info.icu.ucol,
          &iter,
          state,
          (uint8_t *) dest,
          destsize,
          &status);
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("sort key generation failed: %s",
      u_errorName(status))));

 return result;
}

char *
get_collation_actual_version_icu(const char *collcollate)
{
 UCollator  *collator;
 UVersionInfo versioninfo;
 char  buf[U_MAX_VERSION_STRING_LENGTH];

 collator = pg_ucol_open(collcollate);

 ucol_getVersion(collator, versioninfo);
 ucol_close(collator);

 u_versionToString(versioninfo, buf);
 return pstrdup(buf);
}

/*
 * Convert a string in the database encoding into a string of UChars.
 *
 * The source string at buff is of length nbytes
 * (it needn't be nul-terminated)
 *
 * *buff_uchar receives a pointer to the palloc'd result string, and
 * the function's result is the number of UChars generated.
 *
 * The result string is nul-terminated, though most callers rely on the
 * result length instead.
 */

static int32_t
icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
{
 int32_t  len_uchar;

 init_icu_converter();

 len_uchar = uchar_length(icu_converter, buff, nbytes);

 *buff_uchar = palloc_array(UChar, len_uchar + 1);
 len_uchar = uchar_convert(icu_converter,
         *buff_uchar, len_uchar + 1, buff, nbytes);

 return len_uchar;
}

/*
 * Convert a string of UChars into the database encoding.
 *
 * The source string at buff_uchar is of length len_uchar
 * (it needn't be nul-terminated)
 *
 * *result receives a pointer to the palloc'd result string, and the
 * function's result is the number of bytes generated (not counting nul).
 *
 * The result string is nul-terminated.
 */

static size_t
icu_from_uchar(char *dest, size_t destsize, const UChar *buff_uchar, int32_t len_uchar)
{
 UErrorCode status;
 int32_t  len_result;

 init_icu_converter();

 status = U_ZERO_ERROR;
 len_result = ucnv_fromUChars(icu_converter, NULL, 0,
         buff_uchar, len_uchar, &status);
 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
  ereport(ERROR,
    (errmsg("%s failed: %s""ucnv_fromUChars",
      u_errorName(status))));

 if (len_result + 1 > destsize)
  return len_result;

 status = U_ZERO_ERROR;
 len_result = ucnv_fromUChars(icu_converter, dest, len_result + 1,
         buff_uchar, len_uchar, &status);
 if (U_FAILURE(status) ||
  status == U_STRING_NOT_TERMINATED_WARNING)
  ereport(ERROR,
    (errmsg("%s failed: %s""ucnv_fromUChars",
      u_errorName(status))));

 return len_result;
}

static int32_t
icu_convert_case(ICU_Convert_Func func, pg_locale_t mylocale,
     UChar **buff_dest, UChar *buff_source, int32_t len_source)
{
 UErrorCode status;
 int32_t  len_dest;

 len_dest = len_source;  /* try first with same length */
 *buff_dest = palloc_array(UChar, len_dest);
 status = U_ZERO_ERROR;
 len_dest = func(*buff_dest, len_dest, buff_source, len_source,
     mylocale->info.icu.locale, &status);
 if (status == U_BUFFER_OVERFLOW_ERROR)
 {
  /* try again with adjusted length */
  pfree(*buff_dest);
  *buff_dest = palloc_array(UChar, len_dest);
  status = U_ZERO_ERROR;
  len_dest = func(*buff_dest, len_dest, buff_source, len_source,
      mylocale->info.icu.locale, &status);
 }
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("case conversion failed: %s", u_errorName(status))));
 return len_dest;
}

static int32_t
u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
      const UChar *src, int32_t srcLength,
      const char *locale,
      UErrorCode *pErrorCode)
{
 return u_strToTitle(dest, destCapacity, src, srcLength,
      NULL, locale, pErrorCode);
}

static int32_t
u_strFoldCase_default(UChar *dest, int32_t destCapacity,
       const UChar *src, int32_t srcLength,
       const char *locale,
       UErrorCode *pErrorCode)
{
 uint32  options = U_FOLD_CASE_DEFAULT;
 char  lang[ULOC_LANG_CAPACITY];
 UErrorCode status;

 /*
  * Unlike the ICU APIs for lowercasing, titlecasing, and uppercasing, case
  * folding does not accept a locale. Instead it just supports a single
  * option relevant to Turkic languages 'az' and 'tr'; check for those
  * languages to enable the option.
 */

 status = U_ZERO_ERROR;
 uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &status);
 if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING)
 {
  /*
   * The option name is confusing, but it causes u_strFoldCase to use
   * the 'T' mappings, which are ignored for U_FOLD_CASE_DEFAULT.
 */

  if (strcmp(lang, "tr") == 0 || strcmp(lang, "az") == 0)
   options = U_FOLD_CASE_EXCLUDE_SPECIAL_I;
 }

 return u_strFoldCase(dest, destCapacity, src, srcLength,
       options, pErrorCode);
}

/*
 * strncoll_icu
 *
 * Convert the arguments from the database encoding to UChar strings, then
 * call ucol_strcoll(). An argument length of -1 means that the string is
 * NUL-terminated.
 *
 * When the database encoding is UTF-8, and ICU supports ucol_strcollUTF8(),
 * caller should call that instead.
 */

static int
strncoll_icu(const char *arg1, ssize_t len1,
    const char *arg2, ssize_t len2, pg_locale_t locale)
{
 UChar  sbuf[TEXTBUFLEN / sizeof(UChar)];
 UChar    *buf = sbuf;
 int32_t  ulen1;
 int32_t  ulen2;
 size_t  bufsize;
 UChar    *uchar1,
      *uchar2;
 int   result;

 Assert(locale->provider == COLLPROVIDER_ICU);

 /* if encoding is UTF8, use more efficient strncoll_icu_utf8 */
#ifdef HAVE_UCOL_STRCOLLUTF8
 Assert(GetDatabaseEncoding() != PG_UTF8);
#endif

 init_icu_converter();

 ulen1 = uchar_length(icu_converter, arg1, len1);
 ulen2 = uchar_length(icu_converter, arg2, len2);

 /* ulen1+1 or ulen2+1 doesn't risk overflow, but summing them might */
 bufsize = add_size(ulen1 + 1, ulen2 + 1);
 if (bufsize > lengthof(sbuf))
  buf = palloc_array(UChar, bufsize);

 uchar1 = buf;
 uchar2 = buf + ulen1 + 1;

 ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
 ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);

 result = ucol_strcoll(locale->info.icu.ucol,
        uchar1, ulen1,
        uchar2, ulen2);

 if (buf != sbuf)
  pfree(buf);

 return result;
}

/* 'srclen' of -1 means the strings are NUL-terminated */
static size_t
strnxfrm_prefix_icu(char *dest, size_t destsize,
     const char *src, ssize_t srclen,
     pg_locale_t locale)
{
 UChar  sbuf[TEXTBUFLEN / sizeof(UChar)];
 UChar    *uchar = sbuf;
 UCharIterator iter;
 uint32_t state[2];
 UErrorCode status;
 int32_t  ulen;
 Size  result_bsize;

 Assert(locale->provider == COLLPROVIDER_ICU);

 /* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
 Assert(GetDatabaseEncoding() != PG_UTF8);

 init_icu_converter();

 ulen = uchar_length(icu_converter, src, srclen);

 if (ulen >= lengthof(sbuf))
  uchar = palloc_array(UChar, ulen + 1);

 ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);

 uiter_setString(&iter, uchar, ulen);
 state[0] = state[1] = 0/* won't need that again */
 status = U_ZERO_ERROR;
 result_bsize = ucol_nextSortKeyPart(locale->info.icu.ucol,
          &iter,
          state,
          (uint8_t *) dest,
          destsize,
          &status);
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("sort key generation failed: %s",
      u_errorName(status))));

 if (uchar != sbuf)
  pfree(uchar);

 return result_bsize;
}

static void
init_icu_converter(void)
{
 const char *icu_encoding_name;
 UErrorCode status;
 UConverter *conv;

 if (icu_converter)
  return;     /* already done */

 icu_encoding_name = get_encoding_name_for_icu(GetDatabaseEncoding());
 if (!icu_encoding_name)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("encoding \"%s\" not supported by ICU",
      pg_encoding_to_char(GetDatabaseEncoding()))));

 status = U_ZERO_ERROR;
 conv = ucnv_open(icu_encoding_name, &status);
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("could not open ICU converter for encoding \"%s\": %s",
      icu_encoding_name, u_errorName(status))));

 icu_converter = conv;
}

/*
 * Find length, in UChars, of given string if converted to UChar string.
 *
 * A length of -1 indicates that the input string is NUL-terminated.
 *
 * Note: given the assumption that the input string fits in MaxAllocSize,
 * the result cannot overflow int32_t.  But callers must be careful about
 * multiplying the result by sizeof(UChar).
 */

static int32_t
uchar_length(UConverter *converter, const char *str, int32_t len)
{
 UErrorCode status = U_ZERO_ERROR;
 int32_t  ulen;

 ulen = ucnv_toUChars(converter, NULL, 0, str, len, &status);
 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
  ereport(ERROR,
    (errmsg("%s failed: %s""ucnv_toUChars", u_errorName(status))));
 return ulen;
}

/*
 * Convert the given source string into a UChar string, stored in dest, and
 * return the length (in UChars).
 *
 * A srclen of -1 indicates that the input string is NUL-terminated.
 */

static int32_t
uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
     const char *src, int32_t srclen)
{
 UErrorCode status = U_ZERO_ERROR;
 int32_t  ulen;

 ulen = ucnv_toUChars(converter, dest, destlen, src, srclen, &status);
 if (U_FAILURE(status))
  ereport(ERROR,
    (errmsg("%s failed: %s""ucnv_toUChars", u_errorName(status))));
 return ulen;
}

/*
 * Parse collation attributes from the given locale string and apply them to
 * the open collator.
 *
 * First, the locale string is canonicalized to an ICU format locale ID such
 * as "und@colStrength=primary;colCaseLevel=yes". Then, it parses and applies
 * the key-value arguments.
 *
 * Starting with ICU version 54, the attributes are processed automatically by
 * ucol_open(), so this is only necessary for emulating this behavior on older
 * versions.
 */

pg_attribute_unused()
static void
icu_set_collation_attributes(UCollator *collator, const char *loc,
        UErrorCode *status)
{
 int32_t  len;
 char    *icu_locale_id;
 char    *lower_str;
 char    *str;
 char    *token;

 /*
  * The input locale may be a BCP 47 language tag, e.g.
  * "und-u-kc-ks-level1", which expresses the same attributes in a
  * different form. It will be converted to the equivalent ICU format
  * locale ID, e.g. "und@colcaselevel=yes;colstrength=primary", by
  * uloc_canonicalize().
 */

 *status = U_ZERO_ERROR;
 len = uloc_canonicalize(loc, NULL, 0, status);
 icu_locale_id = palloc(len + 1);
 *status = U_ZERO_ERROR;
 len = uloc_canonicalize(loc, icu_locale_id, len + 1, status);
 if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING)
  return;

 lower_str = asc_tolower(icu_locale_id, strlen(icu_locale_id));

 pfree(icu_locale_id);

 str = strchr(lower_str, '@');
 if (!str)
  return;
 str++;

 while ((token = strsep(&str, ";")))
 {
  char    *e = strchr(token, '=');

  if (e)
  {
   char    *name;
   char    *value;
   UColAttribute uattr;
   UColAttributeValue uvalue;

   *status = U_ZERO_ERROR;

   *e = '\0';
   name = token;
   value = e + 1;

   /*
    * See attribute name and value lists in ICU i18n/coll.cpp
 */

   if (strcmp(name, "colstrength") == 0)
    uattr = UCOL_STRENGTH;
   else if (strcmp(name, "colbackwards") == 0)
    uattr = UCOL_FRENCH_COLLATION;
   else if (strcmp(name, "colcaselevel") == 0)
    uattr = UCOL_CASE_LEVEL;
   else if (strcmp(name, "colcasefirst") == 0)
    uattr = UCOL_CASE_FIRST;
   else if (strcmp(name, "colalternate") == 0)
    uattr = UCOL_ALTERNATE_HANDLING;
   else if (strcmp(name, "colnormalization") == 0)
    uattr = UCOL_NORMALIZATION_MODE;
   else if (strcmp(name, "colnumeric") == 0)
    uattr = UCOL_NUMERIC_COLLATION;
   else
    /* ignore if unknown */
    continue;

   if (strcmp(value, "primary") == 0)
    uvalue = UCOL_PRIMARY;
   else if (strcmp(value, "secondary") == 0)
    uvalue = UCOL_SECONDARY;
   else if (strcmp(value, "tertiary") == 0)
    uvalue = UCOL_TERTIARY;
   else if (strcmp(value, "quaternary") == 0)
    uvalue = UCOL_QUATERNARY;
   else if (strcmp(value, "identical") == 0)
    uvalue = UCOL_IDENTICAL;
   else if (strcmp(value, "no") == 0)
    uvalue = UCOL_OFF;
   else if (strcmp(value, "yes") == 0)
    uvalue = UCOL_ON;
   else if (strcmp(value, "shifted") == 0)
    uvalue = UCOL_SHIFTED;
   else if (strcmp(value, "non-ignorable") == 0)
    uvalue = UCOL_NON_IGNORABLE;
   else if (strcmp(value, "lower") == 0)
    uvalue = UCOL_LOWER_FIRST;
   else if (strcmp(value, "upper") == 0)
    uvalue = UCOL_UPPER_FIRST;
   else
   {
    *status = U_ILLEGAL_ARGUMENT_ERROR;
    break;
   }

   ucol_setAttribute(collator, uattr, uvalue, status);
  }
 }

 pfree(lower_str);
}

#endif       /* USE_ICU */

Messung V0.5 in Prozent
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.