LCOV - code coverage report
Current view: top level - src/backend/utils/adt - pg_locale_libc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 51.2 % 328 168
Test Date: 2026-09-12 22:15:41 Functions: 51.1 % 45 23
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 27.8 % 216 60

             Branch data     Line data    Source code
       1                 :             : /*-----------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * PostgreSQL locale utilities for libc
       4                 :             :  *
       5                 :             :  * Portions Copyright (c) 2002-2026, PostgreSQL Global Development Group
       6                 :             :  *
       7                 :             :  * src/backend/utils/adt/pg_locale_libc.c
       8                 :             :  *
       9                 :             :  *-----------------------------------------------------------------------
      10                 :             :  */
      11                 :             : 
      12                 :             : #include "postgres.h"
      13                 :             : 
      14                 :             : #include <limits.h>
      15                 :             : #include <wctype.h>
      16                 :             : 
      17                 :             : #include "access/htup_details.h"
      18                 :             : #include "catalog/pg_database.h"
      19                 :             : #include "catalog/pg_collation.h"
      20                 :             : #include "mb/pg_wchar.h"
      21                 :             : #include "miscadmin.h"
      22                 :             : #include "utils/builtins.h"
      23                 :             : #include "utils/formatting.h"
      24                 :             : #include "utils/memutils.h"
      25                 :             : #include "utils/pg_locale.h"
      26                 :             : #include "utils/syscache.h"
      27                 :             : 
      28                 :             : #ifdef __GLIBC__
      29                 :             : #include <gnu/libc-version.h>
      30                 :             : #endif
      31                 :             : 
      32                 :             : #ifdef WIN32
      33                 :             : #include <shlwapi.h>
      34                 :             : #endif
      35                 :             : 
      36                 :             : /*
      37                 :             :  * For the libc provider, to provide as much functionality as possible on a
      38                 :             :  * variety of platforms without going so far as to implement everything from
      39                 :             :  * scratch, we use several implementation strategies depending on the
      40                 :             :  * situation:
      41                 :             :  *
      42                 :             :  * 1. In C/POSIX collations, we use hard-wired code.  We can't depend on
      43                 :             :  * the <ctype.h> functions since those will obey LC_CTYPE.  Note that these
      44                 :             :  * collations don't give a fig about multibyte characters.
      45                 :             :  *
      46                 :             :  * 2. When working in UTF8 encoding, we use the <wctype.h> functions.
      47                 :             :  * This assumes that every platform uses Unicode codepoints directly
      48                 :             :  * as the wchar_t representation of Unicode.  On some platforms
      49                 :             :  * wchar_t is only 16 bits wide, so we have to punt for codepoints > 0xFFFF.
      50                 :             :  *
      51                 :             :  * 3. In all other encodings, we use the <ctype.h> functions for pg_wchar
      52                 :             :  * values up to 255, and punt for values above that.  This is 100% correct
      53                 :             :  * only in single-byte encodings such as LATINn.  However, non-Unicode
      54                 :             :  * multibyte encodings are mostly Far Eastern character sets for which the
      55                 :             :  * properties being tested here aren't very relevant for higher code values
      56                 :             :  * anyway.  The difficulty with using the <wctype.h> functions with
      57                 :             :  * non-Unicode multibyte encodings is that we can have no certainty that
      58                 :             :  * the platform's wchar_t representation matches what we do in pg_wchar
      59                 :             :  * conversions.
      60                 :             :  *
      61                 :             :  * As a special case, in the "default" collation, (2) and (3) force ASCII
      62                 :             :  * letters to follow ASCII upcase/downcase rules, while in a non-default
      63                 :             :  * collation we just let the library functions do what they will.  The case
      64                 :             :  * where this matters is treatment of I/i in Turkish, and the behavior is
      65                 :             :  * meant to match the upper()/lower() SQL functions.
      66                 :             :  *
      67                 :             :  * NB: the coding here assumes pg_wchar is an unsigned type.
      68                 :             :  */
      69                 :             : 
      70                 :             : /*
      71                 :             :  * Size of stack buffer to use for string transformations, used to avoid heap
      72                 :             :  * allocations in typical cases. This should be large enough that most strings
      73                 :             :  * will fit, but small enough that we feel comfortable putting it on the
      74                 :             :  * stack.
      75                 :             :  */
      76                 :             : #define     TEXTBUFLEN          1024
      77                 :             : 
      78                 :             : extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
      79                 :             : 
      80                 :             : static int  strncoll_libc(const char *arg1, size_t len1,
      81                 :             :                           const char *arg2, size_t len2,
      82                 :             :                           pg_locale_t locale);
      83                 :             : static int  strcoll_libc(const char *arg1, const char *arg2,
      84                 :             :                          pg_locale_t locale);
      85                 :             : static size_t strnxfrm_libc(char *dest, size_t destsize,
      86                 :             :                             const char *src, size_t srclen,
      87                 :             :                             pg_locale_t locale);
      88                 :             : static size_t strxfrm_libc(char *dest, size_t destsize,
      89                 :             :                            const char *src, pg_locale_t locale);
      90                 :             : extern char *get_collation_actual_version_libc(const char *collcollate);
      91                 :             : static locale_t make_libc_collator(const char *collate,
      92                 :             :                                    const char *ctype);
      93                 :             : 
      94                 :             : #ifdef WIN32
      95                 :             : static int  strncoll_libc_win32_utf8(const char *arg1, size_t len1,
      96                 :             :                                      const char *arg2, size_t len2,
      97                 :             :                                      pg_locale_t locale);
      98                 :             : static int  strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
      99                 :             :                                     pg_locale_t locale);
     100                 :             : #endif
     101                 :             : 
     102                 :             : static size_t char2wchar(wchar_t *to, size_t tolen, const char *from,
     103                 :             :                          size_t fromlen, locale_t loc);
     104                 :             : 
     105                 :             : static size_t strlower_libc_sb(char *dest, size_t destsize,
     106                 :             :                                const char *src, size_t srclen,
     107                 :             :                                pg_locale_t locale);
     108                 :             : static size_t strlower_libc_mb(char *dest, size_t destsize,
     109                 :             :                                const char *src, size_t srclen,
     110                 :             :                                pg_locale_t locale);
     111                 :             : static size_t strtitle_libc_sb(char *dest, size_t destsize,
     112                 :             :                                const char *src, size_t srclen,
     113                 :             :                                pg_locale_t locale);
     114                 :             : static size_t strtitle_libc_mb(char *dest, size_t destsize,
     115                 :             :                                const char *src, size_t srclen,
     116                 :             :                                pg_locale_t locale);
     117                 :             : static size_t strupper_libc_sb(char *dest, size_t destsize,
     118                 :             :                                const char *src, size_t srclen,
     119                 :             :                                pg_locale_t locale);
     120                 :             : static size_t strupper_libc_mb(char *dest, size_t destsize,
     121                 :             :                                const char *src, size_t srclen,
     122                 :             :                                pg_locale_t locale);
     123                 :             : 
     124                 :             : static bool
     125                 :           0 : wc_isdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
     126                 :             : {
     127         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     128                 :           0 :         return false;
     129                 :           0 :     return isdigit_l((unsigned char) wc, locale->lt);
     130                 :             : }
     131                 :             : 
     132                 :             : static bool
     133                 :           0 : wc_isalpha_libc_sb(pg_wchar wc, pg_locale_t locale)
     134                 :             : {
     135         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     136                 :           0 :         return false;
     137                 :           0 :     return isalpha_l((unsigned char) wc, locale->lt);
     138                 :             : }
     139                 :             : 
     140                 :             : static bool
     141                 :           0 : wc_isalnum_libc_sb(pg_wchar wc, pg_locale_t locale)
     142                 :             : {
     143         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     144                 :           0 :         return false;
     145                 :           0 :     return isalnum_l((unsigned char) wc, locale->lt);
     146                 :             : }
     147                 :             : 
     148                 :             : static bool
     149                 :           0 : wc_isupper_libc_sb(pg_wchar wc, pg_locale_t locale)
     150                 :             : {
     151         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     152                 :           0 :         return false;
     153                 :           0 :     return isupper_l((unsigned char) wc, locale->lt);
     154                 :             : }
     155                 :             : 
     156                 :             : static bool
     157                 :           0 : wc_islower_libc_sb(pg_wchar wc, pg_locale_t locale)
     158                 :             : {
     159         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     160                 :           0 :         return false;
     161                 :           0 :     return islower_l((unsigned char) wc, locale->lt);
     162                 :             : }
     163                 :             : 
     164                 :             : static bool
     165                 :           0 : wc_isgraph_libc_sb(pg_wchar wc, pg_locale_t locale)
     166                 :             : {
     167         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     168                 :           0 :         return false;
     169                 :           0 :     return isgraph_l((unsigned char) wc, locale->lt);
     170                 :             : }
     171                 :             : 
     172                 :             : static bool
     173                 :           0 : wc_isprint_libc_sb(pg_wchar wc, pg_locale_t locale)
     174                 :             : {
     175         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     176                 :           0 :         return false;
     177                 :           0 :     return isprint_l((unsigned char) wc, locale->lt);
     178                 :             : }
     179                 :             : 
     180                 :             : static bool
     181                 :           0 : wc_ispunct_libc_sb(pg_wchar wc, pg_locale_t locale)
     182                 :             : {
     183         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     184                 :           0 :         return false;
     185                 :           0 :     return ispunct_l((unsigned char) wc, locale->lt);
     186                 :             : }
     187                 :             : 
     188                 :             : static bool
     189                 :           0 : wc_isspace_libc_sb(pg_wchar wc, pg_locale_t locale)
     190                 :             : {
     191         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     192                 :           0 :         return false;
     193                 :           0 :     return isspace_l((unsigned char) wc, locale->lt);
     194                 :             : }
     195                 :             : 
     196                 :             : static bool
     197                 :           0 : wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
     198                 :             : {
     199         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     200                 :           0 :         return false;
     201                 :             : #ifndef WIN32
     202                 :           0 :     return isxdigit_l((unsigned char) wc, locale->lt);
     203                 :             : #else
     204                 :             :     return _isxdigit_l((unsigned char) wc, locale->lt);
     205                 :             : #endif
     206                 :             : }
     207                 :             : 
     208                 :             : static bool
     209                 :           0 : wc_iscased_libc_sb(pg_wchar wc, pg_locale_t locale)
     210                 :             : {
     211         [ #  # ]:           0 :     if (wc > UCHAR_MAX)
     212                 :           0 :         return false;
     213         [ #  # ]:           0 :     return isupper_l((unsigned char) wc, locale->lt) ||
     214         [ #  # ]:           0 :         islower_l((unsigned char) wc, locale->lt);
     215                 :             : }
     216                 :             : 
     217                 :             : static bool
     218                 :      100062 : wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
     219                 :             : {
     220                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     221                 :             :         return false;
     222                 :      100062 :     return iswdigit_l((wint_t) wc, locale->lt);
     223                 :             : }
     224                 :             : 
     225                 :             : static bool
     226                 :       73895 : wc_isalpha_libc_mb(pg_wchar wc, pg_locale_t locale)
     227                 :             : {
     228                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     229                 :             :         return false;
     230                 :       73895 :     return iswalpha_l((wint_t) wc, locale->lt);
     231                 :             : }
     232                 :             : 
     233                 :             : static bool
     234                 :     1642404 : wc_isalnum_libc_mb(pg_wchar wc, pg_locale_t locale)
     235                 :             : {
     236                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     237                 :             :         return false;
     238                 :     1642404 :     return iswalnum_l((wint_t) wc, locale->lt);
     239                 :             : }
     240                 :             : 
     241                 :             : static bool
     242                 :        2056 : wc_isupper_libc_mb(pg_wchar wc, pg_locale_t locale)
     243                 :             : {
     244                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     245                 :             :         return false;
     246                 :        2056 :     return iswupper_l((wint_t) wc, locale->lt);
     247                 :             : }
     248                 :             : 
     249                 :             : static bool
     250                 :        2051 : wc_islower_libc_mb(pg_wchar wc, pg_locale_t locale)
     251                 :             : {
     252                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     253                 :             :         return false;
     254                 :        2051 :     return iswlower_l((wint_t) wc, locale->lt);
     255                 :             : }
     256                 :             : 
     257                 :             : static bool
     258                 :           0 : wc_isgraph_libc_mb(pg_wchar wc, pg_locale_t locale)
     259                 :             : {
     260                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     261                 :             :         return false;
     262                 :           0 :     return iswgraph_l((wint_t) wc, locale->lt);
     263                 :             : }
     264                 :             : 
     265                 :             : static bool
     266                 :           0 : wc_isprint_libc_mb(pg_wchar wc, pg_locale_t locale)
     267                 :             : {
     268                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     269                 :             :         return false;
     270                 :           0 :     return iswprint_l((wint_t) wc, locale->lt);
     271                 :             : }
     272                 :             : 
     273                 :             : static bool
     274                 :        2051 : wc_ispunct_libc_mb(pg_wchar wc, pg_locale_t locale)
     275                 :             : {
     276                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     277                 :             :         return false;
     278                 :        2051 :     return iswpunct_l((wint_t) wc, locale->lt);
     279                 :             : }
     280                 :             : 
     281                 :             : static bool
     282                 :       34486 : wc_isspace_libc_mb(pg_wchar wc, pg_locale_t locale)
     283                 :             : {
     284                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     285                 :             :         return false;
     286                 :       34486 :     return iswspace_l((wint_t) wc, locale->lt);
     287                 :             : }
     288                 :             : 
     289                 :             : static bool
     290                 :           9 : wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
     291                 :             : {
     292                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     293                 :             :         return false;
     294                 :             : #ifndef WIN32
     295                 :           9 :     return iswxdigit_l((wint_t) wc, locale->lt);
     296                 :             : #else
     297                 :             :     return _iswxdigit_l((wint_t) wc, locale->lt);
     298                 :             : #endif
     299                 :             : }
     300                 :             : 
     301                 :             : static bool
     302                 :           0 : wc_iscased_libc_other_mb(pg_wchar wc, pg_locale_t locale)
     303                 :             : {
     304                 :             :     /*
     305                 :             :      * For non-UTF8 multibyte encodings, we conservatively assume that any
     306                 :             :      * non-ASCII character could be case-varying.
     307                 :             :      */
     308         [ #  # ]:           0 :     if (wc > (pg_wchar) 127)
     309                 :           0 :         return true;
     310                 :             : 
     311                 :             :     /* ASCII: pass directly to isupper_l()/islower_l() */
     312         [ #  # ]:           0 :     return isupper_l((unsigned char) wc, locale->lt) ||
     313         [ #  # ]:           0 :         islower_l((unsigned char) wc, locale->lt);
     314                 :             : }
     315                 :             : 
     316                 :             : static bool
     317                 :           0 : wc_iscased_libc_utf8(pg_wchar wc, pg_locale_t locale)
     318                 :             : {
     319                 :             :     /*
     320                 :             :      * If sizeof(wchar_t) < 4 (that is, on Windows), then return false. This
     321                 :             :      * is consistent with the behavior of strlower_libc_mb(): the UTF8 string
     322                 :             :      * will be decoded into 16-bit wchar_t, so strlower_libc_mb() will never
     323                 :             :      * deal with codepoints beyond 0xFFFF. It may deal with surrogate pairs,
     324                 :             :      * but those characters map to themselves anyway.
     325                 :             :      */
     326                 :             :     if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
     327                 :             :         return false;
     328                 :             : 
     329                 :             :     /*
     330                 :             :      * For UTF8, pg_wchar is a codepoint and we assume we can pass it directly
     331                 :             :      * to iswupper_l()/iswlower_l().
     332                 :             :      */
     333   [ #  #  #  # ]:           0 :     return iswupper_l((wint_t) wc, locale->lt) ||
     334                 :           0 :         iswlower_l((wint_t) wc, locale->lt);
     335                 :             : }
     336                 :             : 
     337                 :             : static pg_wchar
     338                 :           0 : toupper_libc_sb(pg_wchar wc, pg_locale_t locale)
     339                 :             : {
     340                 :             :     Assert(GetDatabaseEncoding() != PG_UTF8);
     341                 :             : 
     342                 :             :     /* force C behavior for ASCII characters, per comments above */
     343   [ #  #  #  # ]:           0 :     if (locale->is_default && wc <= (pg_wchar) 127)
     344                 :           0 :         return pg_ascii_toupper((unsigned char) wc);
     345         [ #  # ]:           0 :     else if (wc <= (pg_wchar) UCHAR_MAX)
     346                 :           0 :         return toupper_l((unsigned char) wc, locale->lt);
     347                 :             :     else
     348                 :           0 :         return wc;
     349                 :             : }
     350                 :             : 
     351                 :             : static pg_wchar
     352                 :        4679 : toupper_libc_mb(pg_wchar wc, pg_locale_t locale)
     353                 :             : {
     354                 :             :     Assert(GetDatabaseEncoding() == PG_UTF8);
     355                 :             : 
     356                 :             :     /* force C behavior for ASCII characters, per comments above */
     357   [ +  -  +  + ]:        4679 :     if (locale->is_default && wc <= (pg_wchar) 127)
     358                 :         581 :         return pg_ascii_toupper((unsigned char) wc);
     359                 :             :     else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
     360                 :        4098 :         return towupper_l((wint_t) wc, locale->lt);
     361                 :             :     else
     362                 :             :         return wc;
     363                 :             : }
     364                 :             : 
     365                 :             : static pg_wchar
     366                 :           0 : tolower_libc_sb(pg_wchar wc, pg_locale_t locale)
     367                 :             : {
     368                 :             :     Assert(GetDatabaseEncoding() != PG_UTF8);
     369                 :             : 
     370                 :             :     /* force C behavior for ASCII characters, per comments above */
     371   [ #  #  #  # ]:           0 :     if (locale->is_default && wc <= (pg_wchar) 127)
     372                 :           0 :         return pg_ascii_tolower((unsigned char) wc);
     373         [ #  # ]:           0 :     else if (wc <= (pg_wchar) UCHAR_MAX)
     374                 :           0 :         return tolower_l((unsigned char) wc, locale->lt);
     375                 :             :     else
     376                 :           0 :         return wc;
     377                 :             : }
     378                 :             : 
     379                 :             : static pg_wchar
     380                 :        4681 : tolower_libc_mb(pg_wchar wc, pg_locale_t locale)
     381                 :             : {
     382                 :             :     Assert(GetDatabaseEncoding() == PG_UTF8);
     383                 :             : 
     384                 :             :     /* force C behavior for ASCII characters, per comments above */
     385   [ +  -  +  + ]:        4681 :     if (locale->is_default && wc <= (pg_wchar) 127)
     386                 :         583 :         return pg_ascii_tolower((unsigned char) wc);
     387                 :             :     else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
     388                 :        4098 :         return towlower_l((wint_t) wc, locale->lt);
     389                 :             :     else
     390                 :             :         return wc;
     391                 :             : }
     392                 :             : 
     393                 :             : static const struct ctype_methods ctype_methods_libc_sb = {
     394                 :             :     .strlower = strlower_libc_sb,
     395                 :             :     .strtitle = strtitle_libc_sb,
     396                 :             :     .strupper = strupper_libc_sb,
     397                 :             :     /* in libc, casefolding is the same as lowercasing */
     398                 :             :     .strfold = strlower_libc_sb,
     399                 :             :     .wc_isdigit = wc_isdigit_libc_sb,
     400                 :             :     .wc_isalpha = wc_isalpha_libc_sb,
     401                 :             :     .wc_isalnum = wc_isalnum_libc_sb,
     402                 :             :     .wc_isupper = wc_isupper_libc_sb,
     403                 :             :     .wc_islower = wc_islower_libc_sb,
     404                 :             :     .wc_isgraph = wc_isgraph_libc_sb,
     405                 :             :     .wc_isprint = wc_isprint_libc_sb,
     406                 :             :     .wc_ispunct = wc_ispunct_libc_sb,
     407                 :             :     .wc_isspace = wc_isspace_libc_sb,
     408                 :             :     .wc_isxdigit = wc_isxdigit_libc_sb,
     409                 :             :     .wc_iscased = wc_iscased_libc_sb,
     410                 :             :     .wc_toupper = toupper_libc_sb,
     411                 :             :     .wc_tolower = tolower_libc_sb,
     412                 :             : };
     413                 :             : 
     414                 :             : /*
     415                 :             :  * Non-UTF8 multibyte encodings use multibyte semantics for case mapping, but
     416                 :             :  * single-byte semantics for pattern matching (except wc_iscased which needs
     417                 :             :  * to be consistent with case mapping).
     418                 :             :  */
     419                 :             : static const struct ctype_methods ctype_methods_libc_other_mb = {
     420                 :             :     .strlower = strlower_libc_mb,
     421                 :             :     .strtitle = strtitle_libc_mb,
     422                 :             :     .strupper = strupper_libc_mb,
     423                 :             :     /* in libc, casefolding is the same as lowercasing */
     424                 :             :     .strfold = strlower_libc_mb,
     425                 :             :     .wc_isdigit = wc_isdigit_libc_sb,
     426                 :             :     .wc_isalpha = wc_isalpha_libc_sb,
     427                 :             :     .wc_isalnum = wc_isalnum_libc_sb,
     428                 :             :     .wc_isupper = wc_isupper_libc_sb,
     429                 :             :     .wc_islower = wc_islower_libc_sb,
     430                 :             :     .wc_isgraph = wc_isgraph_libc_sb,
     431                 :             :     .wc_isprint = wc_isprint_libc_sb,
     432                 :             :     .wc_ispunct = wc_ispunct_libc_sb,
     433                 :             :     .wc_isspace = wc_isspace_libc_sb,
     434                 :             :     .wc_isxdigit = wc_isxdigit_libc_sb,
     435                 :             :     .wc_iscased = wc_iscased_libc_other_mb,
     436                 :             :     .wc_toupper = toupper_libc_sb,
     437                 :             :     .wc_tolower = tolower_libc_sb,
     438                 :             : };
     439                 :             : 
     440                 :             : static const struct ctype_methods ctype_methods_libc_utf8 = {
     441                 :             :     .strlower = strlower_libc_mb,
     442                 :             :     .strtitle = strtitle_libc_mb,
     443                 :             :     .strupper = strupper_libc_mb,
     444                 :             :     /* in libc, casefolding is the same as lowercasing */
     445                 :             :     .strfold = strlower_libc_mb,
     446                 :             :     .wc_isdigit = wc_isdigit_libc_mb,
     447                 :             :     .wc_isalpha = wc_isalpha_libc_mb,
     448                 :             :     .wc_isalnum = wc_isalnum_libc_mb,
     449                 :             :     .wc_isupper = wc_isupper_libc_mb,
     450                 :             :     .wc_islower = wc_islower_libc_mb,
     451                 :             :     .wc_isgraph = wc_isgraph_libc_mb,
     452                 :             :     .wc_isprint = wc_isprint_libc_mb,
     453                 :             :     .wc_ispunct = wc_ispunct_libc_mb,
     454                 :             :     .wc_isspace = wc_isspace_libc_mb,
     455                 :             :     .wc_isxdigit = wc_isxdigit_libc_mb,
     456                 :             :     .wc_iscased = wc_iscased_libc_utf8,
     457                 :             :     .wc_toupper = toupper_libc_mb,
     458                 :             :     .wc_tolower = tolower_libc_mb,
     459                 :             : };
     460                 :             : 
     461                 :             : static const struct collate_methods collate_methods_libc = {
     462                 :             :     .strncoll = strncoll_libc,
     463                 :             :     .strcoll = strcoll_libc,
     464                 :             :     .strnxfrm = strnxfrm_libc,
     465                 :             :     .strxfrm = strxfrm_libc,
     466                 :             :     .strnxfrm_prefix = NULL,
     467                 :             :     .strxfrm_prefix = NULL,
     468                 :             : 
     469                 :             :     /*
     470                 :             :      * Unfortunately, it seems that strxfrm() for non-C collations is broken
     471                 :             :      * on many common platforms; testing of multiple versions of glibc reveals
     472                 :             :      * that, for many locales, strcoll() and strxfrm() do not return
     473                 :             :      * consistent results. While no other libc other than Cygwin has so far
     474                 :             :      * been shown to have a problem, we take the conservative course of action
     475                 :             :      * for right now and disable this categorically.  (Users who are certain
     476                 :             :      * this isn't a problem on their system can define TRUST_STRXFRM.)
     477                 :             :      */
     478                 :             : #ifdef TRUST_STRXFRM
     479                 :             :     .strxfrm_is_safe = true,
     480                 :             : #else
     481                 :             :     .strxfrm_is_safe = false,
     482                 :             : #endif
     483                 :             : };
     484                 :             : 
     485                 :             : #ifdef WIN32
     486                 :             : static const struct collate_methods collate_methods_libc_win32_utf8 = {
     487                 :             :     .strncoll = strncoll_libc_win32_utf8,
     488                 :             :     .strcoll = strcoll_libc_win32_utf8,
     489                 :             :     .strnxfrm = strnxfrm_libc,
     490                 :             :     .strxfrm = strxfrm_libc,
     491                 :             :     .strnxfrm_prefix = NULL,
     492                 :             : #ifdef TRUST_STRXFRM
     493                 :             :     .strxfrm_is_safe = true,
     494                 :             : #else
     495                 :             :     .strxfrm_is_safe = false,
     496                 :             : #endif
     497                 :             : };
     498                 :             : #endif
     499                 :             : 
     500                 :             : static size_t
     501                 :           0 : strlower_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
     502                 :             :                  pg_locale_t locale)
     503                 :             : {
     504         [ #  # ]:           0 :     if (srclen + 1 <= destsize)
     505                 :             :     {
     506                 :           0 :         locale_t    loc = locale->lt;
     507                 :             :         char       *p;
     508                 :             : 
     509                 :           0 :         memcpy(dest, src, srclen);
     510                 :           0 :         dest[srclen] = '\0';
     511                 :             : 
     512                 :             :         /*
     513                 :             :          * Note: we assume that tolower_l() will not be so broken as to need
     514                 :             :          * an isupper_l() guard test.  When using the default collation, we
     515                 :             :          * apply the traditional Postgres behavior that forces ASCII-style
     516                 :             :          * treatment of I/i, but in non-default collations you get exactly
     517                 :             :          * what the collation says.
     518                 :             :          */
     519         [ #  # ]:           0 :         for (p = dest; *p; p++)
     520                 :             :         {
     521         [ #  # ]:           0 :             if (locale->is_default)
     522                 :             :             {
     523   [ #  #  #  # ]:           0 :                 if (*p >= 'A' && *p <= 'Z')
     524                 :           0 :                     *p += 'a' - 'A';
     525   [ #  #  #  # ]:           0 :                 else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
     526                 :           0 :                     *p = tolower_l((unsigned char) *p, loc);
     527                 :             :             }
     528                 :             :             else
     529                 :           0 :                 *p = tolower_l((unsigned char) *p, loc);
     530                 :             :         }
     531                 :             :     }
     532                 :             : 
     533                 :           0 :     return srclen;
     534                 :             : }
     535                 :             : 
     536                 :             : static size_t
     537                 :      522381 : strlower_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
     538                 :             :                  pg_locale_t locale)
     539                 :             : {
     540                 :      522381 :     locale_t    loc = locale->lt;
     541                 :             :     size_t      result_size;
     542                 :             :     wchar_t    *workspace;
     543                 :             :     char       *result;
     544                 :             :     size_t      curr_char;
     545                 :             :     size_t      max_size;
     546                 :             : 
     547                 :             :     /* Overflow paranoia */
     548         [ -  + ]:      522381 :     if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
     549         [ #  # ]:           0 :         ereport(ERROR,
     550                 :             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     551                 :             :                  errmsg("out of memory")));
     552                 :             : 
     553                 :             :     /* Output workspace cannot have more codes than input bytes */
     554                 :      522381 :     workspace = palloc_array(wchar_t, srclen + 1);
     555                 :             : 
     556                 :      522381 :     char2wchar(workspace, srclen + 1, src, srclen, loc);
     557                 :             : 
     558         [ +  + ]:     2500106 :     for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
     559                 :     1977725 :         workspace[curr_char] = towlower_l(workspace[curr_char], loc);
     560                 :             : 
     561                 :             :     /*
     562                 :             :      * Make result large enough; case change might change number of bytes
     563                 :             :      */
     564                 :      522381 :     max_size = curr_char * pg_database_encoding_max_length();
     565                 :      522381 :     result = palloc(max_size + 1);
     566                 :             : 
     567                 :      522381 :     result_size = wchar2char(result, workspace, max_size + 1, loc);
     568                 :             : 
     569         [ +  - ]:      522381 :     if (destsize >= result_size + 1)
     570                 :             :     {
     571                 :      522381 :         memcpy(dest, result, result_size);
     572                 :      522381 :         dest[result_size] = '\0';
     573                 :             :     }
     574                 :             : 
     575                 :      522381 :     pfree(workspace);
     576                 :      522381 :     pfree(result);
     577                 :             : 
     578                 :      522381 :     return result_size;
     579                 :             : }
     580                 :             : 
     581                 :             : static size_t
     582                 :           0 : strtitle_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
     583                 :             :                  pg_locale_t locale)
     584                 :             : {
     585         [ #  # ]:           0 :     if (srclen + 1 <= destsize)
     586                 :             :     {
     587                 :           0 :         locale_t    loc = locale->lt;
     588                 :           0 :         int         wasalnum = false;
     589                 :             :         char       *p;
     590                 :             : 
     591                 :           0 :         memcpy(dest, src, srclen);
     592                 :           0 :         dest[srclen] = '\0';
     593                 :             : 
     594                 :             :         /*
     595                 :             :          * Note: we assume that toupper_l()/tolower_l() will not be so broken
     596                 :             :          * as to need guard tests.  When using the default collation, we apply
     597                 :             :          * the traditional Postgres behavior that forces ASCII-style treatment
     598                 :             :          * of I/i, but in non-default collations you get exactly what the
     599                 :             :          * collation says.
     600                 :             :          */
     601         [ #  # ]:           0 :         for (p = dest; *p; p++)
     602                 :             :         {
     603         [ #  # ]:           0 :             if (locale->is_default)
     604                 :             :             {
     605         [ #  # ]:           0 :                 if (wasalnum)
     606                 :             :                 {
     607   [ #  #  #  # ]:           0 :                     if (*p >= 'A' && *p <= 'Z')
     608                 :           0 :                         *p += 'a' - 'A';
     609   [ #  #  #  # ]:           0 :                     else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
     610                 :           0 :                         *p = tolower_l((unsigned char) *p, loc);
     611                 :             :                 }
     612                 :             :                 else
     613                 :             :                 {
     614   [ #  #  #  # ]:           0 :                     if (*p >= 'a' && *p <= 'z')
     615                 :           0 :                         *p -= 'a' - 'A';
     616   [ #  #  #  # ]:           0 :                     else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
     617                 :           0 :                         *p = toupper_l((unsigned char) *p, loc);
     618                 :             :                 }
     619                 :             :             }
     620                 :             :             else
     621                 :             :             {
     622         [ #  # ]:           0 :                 if (wasalnum)
     623                 :           0 :                     *p = tolower_l((unsigned char) *p, loc);
     624                 :             :                 else
     625                 :           0 :                     *p = toupper_l((unsigned char) *p, loc);
     626                 :             :             }
     627                 :           0 :             wasalnum = isalnum_l((unsigned char) *p, loc);
     628                 :             :         }
     629                 :             :     }
     630                 :             : 
     631                 :           0 :     return srclen;
     632                 :             : }
     633                 :             : 
     634                 :             : static size_t
     635                 :          18 : strtitle_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
     636                 :             :                  pg_locale_t locale)
     637                 :             : {
     638                 :          18 :     locale_t    loc = locale->lt;
     639                 :          18 :     int         wasalnum = false;
     640                 :             :     size_t      result_size;
     641                 :             :     wchar_t    *workspace;
     642                 :             :     char       *result;
     643                 :             :     size_t      curr_char;
     644                 :             :     size_t      max_size;
     645                 :             : 
     646                 :             :     /* Overflow paranoia */
     647         [ -  + ]:          18 :     if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
     648         [ #  # ]:           0 :         ereport(ERROR,
     649                 :             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     650                 :             :                  errmsg("out of memory")));
     651                 :             : 
     652                 :             :     /* Output workspace cannot have more codes than input bytes */
     653                 :          18 :     workspace = palloc_array(wchar_t, srclen + 1);
     654                 :             : 
     655                 :          18 :     char2wchar(workspace, srclen + 1, src, srclen, loc);
     656                 :             : 
     657         [ +  + ]:         165 :     for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
     658                 :             :     {
     659         [ +  + ]:         147 :         if (wasalnum)
     660                 :         111 :             workspace[curr_char] = towlower_l(workspace[curr_char], loc);
     661                 :             :         else
     662                 :          36 :             workspace[curr_char] = towupper_l(workspace[curr_char], loc);
     663                 :         147 :         wasalnum = iswalnum_l(workspace[curr_char], loc);
     664                 :             :     }
     665                 :             : 
     666                 :             :     /*
     667                 :             :      * Make result large enough; case change might change number of bytes
     668                 :             :      */
     669                 :          18 :     max_size = curr_char * pg_database_encoding_max_length();
     670                 :          18 :     result = palloc(max_size + 1);
     671                 :             : 
     672                 :          18 :     result_size = wchar2char(result, workspace, max_size + 1, loc);
     673                 :             : 
     674         [ +  - ]:          18 :     if (destsize >= result_size + 1)
     675                 :             :     {
     676                 :          18 :         memcpy(dest, result, result_size);
     677                 :          18 :         dest[result_size] = '\0';
     678                 :             :     }
     679                 :             : 
     680                 :          18 :     pfree(workspace);
     681                 :          18 :     pfree(result);
     682                 :             : 
     683                 :          18 :     return result_size;
     684                 :             : }
     685                 :             : 
     686                 :             : static size_t
     687                 :           0 : strupper_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
     688                 :             :                  pg_locale_t locale)
     689                 :             : {
     690         [ #  # ]:           0 :     if (srclen + 1 <= destsize)
     691                 :             :     {
     692                 :           0 :         locale_t    loc = locale->lt;
     693                 :             :         char       *p;
     694                 :             : 
     695                 :           0 :         memcpy(dest, src, srclen);
     696                 :           0 :         dest[srclen] = '\0';
     697                 :             : 
     698                 :             :         /*
     699                 :             :          * Note: we assume that toupper_l() will not be so broken as to need
     700                 :             :          * an islower_l() guard test.  When using the default collation, we
     701                 :             :          * apply the traditional Postgres behavior that forces ASCII-style
     702                 :             :          * treatment of I/i, but in non-default collations you get exactly
     703                 :             :          * what the collation says.
     704                 :             :          */
     705         [ #  # ]:           0 :         for (p = dest; *p; p++)
     706                 :             :         {
     707         [ #  # ]:           0 :             if (locale->is_default)
     708                 :             :             {
     709   [ #  #  #  # ]:           0 :                 if (*p >= 'a' && *p <= 'z')
     710                 :           0 :                     *p -= 'a' - 'A';
     711   [ #  #  #  # ]:           0 :                 else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
     712                 :           0 :                     *p = toupper_l((unsigned char) *p, loc);
     713                 :             :             }
     714                 :             :             else
     715                 :           0 :                 *p = toupper_l((unsigned char) *p, loc);
     716                 :             :         }
     717                 :             :     }
     718                 :             : 
     719                 :           0 :     return srclen;
     720                 :             : }
     721                 :             : 
     722                 :             : static size_t
     723                 :      522091 : strupper_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
     724                 :             :                  pg_locale_t locale)
     725                 :             : {
     726                 :      522091 :     locale_t    loc = locale->lt;
     727                 :             :     size_t      result_size;
     728                 :             :     wchar_t    *workspace;
     729                 :             :     char       *result;
     730                 :             :     size_t      curr_char;
     731                 :             :     size_t      max_size;
     732                 :             : 
     733                 :             :     /* Overflow paranoia */
     734         [ -  + ]:      522091 :     if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
     735         [ #  # ]:           0 :         ereport(ERROR,
     736                 :             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     737                 :             :                  errmsg("out of memory")));
     738                 :             : 
     739                 :             :     /* Output workspace cannot have more codes than input bytes */
     740                 :      522091 :     workspace = palloc_array(wchar_t, srclen + 1);
     741                 :             : 
     742                 :      522091 :     char2wchar(workspace, srclen + 1, src, srclen, loc);
     743                 :             : 
     744         [ +  + ]:     1654042 :     for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
     745                 :     1131951 :         workspace[curr_char] = towupper_l(workspace[curr_char], loc);
     746                 :             : 
     747                 :             :     /*
     748                 :             :      * Make result large enough; case change might change number of bytes
     749                 :             :      */
     750                 :      522091 :     max_size = curr_char * pg_database_encoding_max_length();
     751                 :      522091 :     result = palloc(max_size + 1);
     752                 :             : 
     753                 :      522091 :     result_size = wchar2char(result, workspace, max_size + 1, loc);
     754                 :             : 
     755         [ +  - ]:      522091 :     if (destsize >= result_size + 1)
     756                 :             :     {
     757                 :      522091 :         memcpy(dest, result, result_size);
     758                 :      522091 :         dest[result_size] = '\0';
     759                 :             :     }
     760                 :             : 
     761                 :      522091 :     pfree(workspace);
     762                 :      522091 :     pfree(result);
     763                 :             : 
     764                 :      522091 :     return result_size;
     765                 :             : }
     766                 :             : 
     767                 :             : pg_locale_t
     768                 :       18363 : create_pg_locale_libc(Oid collid, MemoryContext context)
     769                 :             : {
     770                 :             :     const char *collate;
     771                 :             :     const char *ctype;
     772                 :             :     locale_t    loc;
     773                 :             :     pg_locale_t result;
     774                 :             : 
     775         [ +  + ]:       18363 :     if (collid == DEFAULT_COLLATION_OID)
     776                 :             :     {
     777                 :             :         HeapTuple   tp;
     778                 :             :         Datum       datum;
     779                 :             : 
     780                 :       18307 :         tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
     781         [ -  + ]:       18307 :         if (!HeapTupleIsValid(tp))
     782         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
     783                 :       18307 :         datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
     784                 :             :                                        Anum_pg_database_datcollate);
     785                 :       18307 :         collate = TextDatumGetCString(datum);
     786                 :       18307 :         datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
     787                 :             :                                        Anum_pg_database_datctype);
     788                 :       18307 :         ctype = TextDatumGetCString(datum);
     789                 :             : 
     790                 :       18307 :         ReleaseSysCache(tp);
     791                 :             :     }
     792                 :             :     else
     793                 :             :     {
     794                 :             :         HeapTuple   tp;
     795                 :             :         Datum       datum;
     796                 :             : 
     797                 :          56 :         tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
     798         [ -  + ]:          56 :         if (!HeapTupleIsValid(tp))
     799         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for collation %u", collid);
     800                 :             : 
     801                 :          56 :         datum = SysCacheGetAttrNotNull(COLLOID, tp,
     802                 :             :                                        Anum_pg_collation_collcollate);
     803                 :          56 :         collate = TextDatumGetCString(datum);
     804                 :          56 :         datum = SysCacheGetAttrNotNull(COLLOID, tp,
     805                 :             :                                        Anum_pg_collation_collctype);
     806                 :          56 :         ctype = TextDatumGetCString(datum);
     807                 :             : 
     808                 :          56 :         ReleaseSysCache(tp);
     809                 :             :     }
     810                 :             : 
     811                 :             : 
     812                 :       18363 :     loc = make_libc_collator(collate, ctype);
     813                 :             : 
     814                 :       18363 :     result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
     815                 :       18363 :     result->deterministic = true;
     816         [ +  + ]:       36153 :     result->collate_is_c = (strcmp(collate, "C") == 0) ||
     817         [ +  + ]:       17790 :         (strcmp(collate, "POSIX") == 0);
     818         [ +  + ]:       36153 :     result->ctype_is_c = (strcmp(ctype, "C") == 0) ||
     819         [ +  + ]:       17790 :         (strcmp(ctype, "POSIX") == 0);
     820                 :       18363 :     result->lt = loc;
     821         [ +  + ]:       18363 :     if (!result->collate_is_c)
     822                 :             :     {
     823                 :             : #ifdef WIN32
     824                 :             :         if (GetDatabaseEncoding() == PG_UTF8)
     825                 :             :             result->collate = &collate_methods_libc_win32_utf8;
     826                 :             :         else
     827                 :             : #endif
     828                 :       17750 :             result->collate = &collate_methods_libc;
     829                 :             :     }
     830         [ +  + ]:       18363 :     if (!result->ctype_is_c)
     831                 :             :     {
     832         [ +  + ]:       17750 :         if (GetDatabaseEncoding() == PG_UTF8)
     833                 :       17710 :             result->ctype = &ctype_methods_libc_utf8;
     834         [ -  + ]:          40 :         else if (pg_database_encoding_max_length() > 1)
     835                 :           0 :             result->ctype = &ctype_methods_libc_other_mb;
     836                 :             :         else
     837                 :          40 :             result->ctype = &ctype_methods_libc_sb;
     838                 :             :     }
     839                 :             : 
     840                 :       18363 :     return result;
     841                 :             : }
     842                 :             : 
     843                 :             : /*
     844                 :             :  * Create a locale_t with the given collation and ctype.
     845                 :             :  *
     846                 :             :  * The "C" and "POSIX" locales are not actually handled by libc, so return
     847                 :             :  * NULL.
     848                 :             :  *
     849                 :             :  * Ensure that no path leaks a locale_t.
     850                 :             :  */
     851                 :             : static locale_t
     852                 :       18363 : make_libc_collator(const char *collate, const char *ctype)
     853                 :             : {
     854                 :       18363 :     locale_t    loc = 0;
     855                 :             : 
     856         [ +  - ]:       18363 :     if (strcmp(collate, ctype) == 0)
     857                 :             :     {
     858   [ +  +  +  + ]:       18363 :         if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
     859                 :             :         {
     860                 :             :             /* Normal case where they're the same */
     861                 :       17750 :             errno = 0;
     862                 :             : #ifndef WIN32
     863                 :       17750 :             loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
     864                 :             :                             NULL);
     865                 :             : #else
     866                 :             :             loc = _create_locale(LC_ALL, collate);
     867                 :             : #endif
     868         [ -  + ]:       17750 :             if (!loc)
     869                 :           0 :                 report_newlocale_failure(collate);
     870                 :             :         }
     871                 :             :     }
     872                 :             :     else
     873                 :             :     {
     874                 :             : #ifndef WIN32
     875                 :             :         /* We need two newlocale() steps */
     876                 :           0 :         locale_t    loc1 = 0;
     877                 :             : 
     878   [ #  #  #  # ]:           0 :         if (strcmp(collate, "C") != 0 && strcmp(collate, "POSIX") != 0)
     879                 :             :         {
     880                 :           0 :             errno = 0;
     881                 :           0 :             loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
     882         [ #  # ]:           0 :             if (!loc1)
     883                 :           0 :                 report_newlocale_failure(collate);
     884                 :             :         }
     885                 :             : 
     886   [ #  #  #  # ]:           0 :         if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
     887                 :             :         {
     888                 :           0 :             errno = 0;
     889                 :           0 :             loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
     890         [ #  # ]:           0 :             if (!loc)
     891                 :             :             {
     892         [ #  # ]:           0 :                 if (loc1)
     893                 :           0 :                     freelocale(loc1);
     894                 :           0 :                 report_newlocale_failure(ctype);
     895                 :             :             }
     896                 :             :         }
     897                 :             :         else
     898                 :           0 :             loc = loc1;
     899                 :             : #else
     900                 :             : 
     901                 :             :         /*
     902                 :             :          * XXX The _create_locale() API doesn't appear to support this. Could
     903                 :             :          * perhaps be worked around by changing pg_locale_t to contain two
     904                 :             :          * separate fields.
     905                 :             :          */
     906                 :             :         ereport(ERROR,
     907                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     908                 :             :                  errmsg("collations with different collate and ctype values are not supported on this platform")));
     909                 :             : #endif
     910                 :             :     }
     911                 :             : 
     912                 :       18363 :     return loc;
     913                 :             : }
     914                 :             : 
     915                 :             : /*
     916                 :             :  * strncoll_libc
     917                 :             :  *
     918                 :             :  * NUL-terminate arguments and pass to strcoll_l().
     919                 :             :  */
     920                 :             : static int
     921                 :     2784107 : strncoll_libc(const char *arg1, size_t len1, const char *arg2, size_t len2,
     922                 :             :               pg_locale_t locale)
     923                 :             : {
     924                 :             :     char        sbuf[TEXTBUFLEN];
     925                 :     2784107 :     char       *buf = sbuf;
     926                 :     2784107 :     size_t      bufsize1 = len1 + 1;
     927                 :     2784107 :     size_t      bufsize2 = len2 + 1;
     928                 :             :     char       *buf1;
     929                 :             :     char       *buf2;
     930                 :             :     const char *arg1n;
     931                 :             :     const char *arg2n;
     932                 :             :     int         result;
     933                 :             : 
     934         [ +  + ]:     2784107 :     if (bufsize1 + bufsize2 > TEXTBUFLEN)
     935                 :         318 :         buf = palloc(bufsize1 + bufsize2);
     936                 :             : 
     937                 :     2784107 :     buf1 = buf;
     938                 :     2784107 :     buf2 = buf + bufsize1;
     939                 :             : 
     940                 :     2784107 :     memcpy(buf1, arg1, len1);
     941                 :     2784107 :     buf1[len1] = '\0';
     942                 :     2784107 :     arg1n = buf1;
     943                 :             : 
     944                 :     2784107 :     memcpy(buf2, arg2, len2);
     945                 :     2784107 :     buf2[len2] = '\0';
     946                 :     2784107 :     arg2n = buf2;
     947                 :             : 
     948                 :     2784107 :     result = strcoll_l(arg1n, arg2n, locale->lt);
     949                 :             : 
     950         [ +  + ]:     2784107 :     if (buf != sbuf)
     951                 :         318 :         pfree(buf);
     952                 :             : 
     953                 :     2784107 :     return result;
     954                 :             : }
     955                 :             : 
     956                 :             : /*
     957                 :             :  * strcoll_libc
     958                 :             :  */
     959                 :             : static int
     960                 :    15087949 : strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
     961                 :             : {
     962                 :    15087949 :     return strcoll_l(arg1, arg2, locale->lt);
     963                 :             : }
     964                 :             : 
     965                 :             : /*
     966                 :             :  * strnxfrm_libc
     967                 :             :  *
     968                 :             :  * NUL-terminate src and pass to strxfrm_l().
     969                 :             :  *
     970                 :             :  * NB: it's possible for this function to return a different size needed for
     971                 :             :  * two calls with the same input string. If destsize is too small to hold the
     972                 :             :  * result, strxfrm() may return the upper bound of the size needed rather than
     973                 :             :  * the exact size needed.
     974                 :             :  */
     975                 :             : static size_t
     976                 :           0 : strnxfrm_libc(char *dest, size_t destsize, const char *src, size_t srclen,
     977                 :             :               pg_locale_t locale)
     978                 :             : {
     979                 :             :     char        sbuf[TEXTBUFLEN];
     980                 :           0 :     char       *buf = sbuf;
     981                 :           0 :     size_t      bufsize = srclen + 1;
     982                 :             :     size_t      result;
     983                 :             : 
     984         [ #  # ]:           0 :     if (bufsize > TEXTBUFLEN)
     985                 :           0 :         buf = palloc(bufsize);
     986                 :             : 
     987                 :             :     /* nul-terminate argument */
     988                 :           0 :     memcpy(buf, src, srclen);
     989                 :           0 :     buf[srclen] = '\0';
     990                 :             : 
     991                 :           0 :     result = strxfrm_l(dest, buf, destsize, locale->lt);
     992                 :             : 
     993         [ #  # ]:           0 :     if (buf != sbuf)
     994                 :           0 :         pfree(buf);
     995                 :             : 
     996                 :             :     /* if dest is defined, it should be nul-terminated */
     997                 :             :     Assert(result >= destsize || dest[result] == '\0');
     998                 :             : 
     999                 :           0 :     return result;
    1000                 :             : }
    1001                 :             : 
    1002                 :             : /*
    1003                 :             :  * strxfrm_libc
    1004                 :             :  *
    1005                 :             :  * NB: it's possible for this function to return a different size needed for
    1006                 :             :  * two calls with the same input string. If destsize is too small to hold the
    1007                 :             :  * result, strxfrm() may return the upper bound of the size needed rather than
    1008                 :             :  * the exact size needed.
    1009                 :             :  */
    1010                 :             : static size_t
    1011                 :         132 : strxfrm_libc(char *dest, size_t destsize, const char *src, pg_locale_t locale)
    1012                 :             : {
    1013                 :         132 :     return strxfrm_l(dest, src, destsize, locale->lt);
    1014                 :             : }
    1015                 :             : 
    1016                 :             : char *
    1017                 :       18131 : get_collation_actual_version_libc(const char *collcollate)
    1018                 :             : {
    1019                 :       18131 :     char       *collversion = NULL;
    1020                 :             : 
    1021   [ +  +  +  + ]:       36170 :     if (pg_strcasecmp("C", collcollate) != 0 &&
    1022         [ +  + ]:       35968 :         pg_strncasecmp("C.", collcollate, 2) != 0 &&
    1023                 :       17929 :         pg_strcasecmp("POSIX", collcollate) != 0)
    1024                 :             :     {
    1025                 :             : #if defined(__GLIBC__)
    1026                 :             :         /* Use the glibc version because we don't have anything better. */
    1027                 :       17915 :         collversion = pstrdup(gnu_get_libc_version());
    1028                 :             : #elif defined(LC_VERSION_MASK)
    1029                 :             :         locale_t    loc;
    1030                 :             : 
    1031                 :             :         /* Look up FreeBSD collation version. */
    1032                 :             :         loc = newlocale(LC_COLLATE_MASK, collcollate, NULL);
    1033                 :             :         if (loc)
    1034                 :             :         {
    1035                 :             :             collversion =
    1036                 :             :                 pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
    1037                 :             :             freelocale(loc);
    1038                 :             :         }
    1039                 :             :         else
    1040                 :             :             ereport(ERROR,
    1041                 :             :                     (errmsg("could not load locale \"%s\"", collcollate)));
    1042                 :             : #elif defined(WIN32)
    1043                 :             :         /*
    1044                 :             :          * If we are targeting Windows Vista and above, we can ask for a name
    1045                 :             :          * given a collation name (earlier versions required a location code
    1046                 :             :          * that we don't have).
    1047                 :             :          */
    1048                 :             :         NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
    1049                 :             :         WCHAR       wide_collcollate[LOCALE_NAME_MAX_LENGTH];
    1050                 :             : 
    1051                 :             :         MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
    1052                 :             :                             LOCALE_NAME_MAX_LENGTH);
    1053                 :             :         if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
    1054                 :             :         {
    1055                 :             :             /*
    1056                 :             :              * GetNLSVersionEx() wants a language tag such as "en-US", not a
    1057                 :             :              * locale name like "English_United States.1252".  Until those
    1058                 :             :              * values can be prevented from entering the system, or 100%
    1059                 :             :              * reliably converted to the more useful tag format, tolerate the
    1060                 :             :              * resulting error and report that we have no version data.
    1061                 :             :              */
    1062                 :             :             if (GetLastError() == ERROR_INVALID_PARAMETER)
    1063                 :             :                 return NULL;
    1064                 :             : 
    1065                 :             :             ereport(ERROR,
    1066                 :             :                     (errmsg("could not get collation version for locale \"%s\": error code %lu",
    1067                 :             :                             collcollate,
    1068                 :             :                             GetLastError())));
    1069                 :             :         }
    1070                 :             :         collversion = psprintf("%lu.%lu,%lu.%lu",
    1071                 :             :                                (version.dwNLSVersion >> 8) & 0xFFFF,
    1072                 :             :                                version.dwNLSVersion & 0xFF,
    1073                 :             :                                (version.dwDefinedVersion >> 8) & 0xFFFF,
    1074                 :             :                                version.dwDefinedVersion & 0xFF);
    1075                 :             : #endif
    1076                 :             :     }
    1077                 :             : 
    1078                 :       18131 :     return collversion;
    1079                 :             : }
    1080                 :             : 
    1081                 :             : /*
    1082                 :             :  * strncoll_libc_win32_utf8
    1083                 :             :  *
    1084                 :             :  * Win32 does not have UTF-8. Convert UTF8 arguments to wide characters and
    1085                 :             :  * invoke wcscoll_l().
    1086                 :             :  */
    1087                 :             : #ifdef WIN32
    1088                 :             : static int
    1089                 :             : strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2,
    1090                 :             :                          size_t len2, pg_locale_t locale)
    1091                 :             : {
    1092                 :             :     char        sbuf[TEXTBUFLEN];
    1093                 :             :     char       *buf = sbuf;
    1094                 :             :     char       *a1p,
    1095                 :             :                *a2p;
    1096                 :             :     size_t      a1len,
    1097                 :             :                 a2len,
    1098                 :             :                 buflen;
    1099                 :             :     int         r;
    1100                 :             :     int         result;
    1101                 :             : 
    1102                 :             :     Assert(GetDatabaseEncoding() == PG_UTF8);
    1103                 :             : 
    1104                 :             :     /*
    1105                 :             :      * In a 32-bit build, twice the input length can overflow size_t, so we
    1106                 :             :      * must be careful.
    1107                 :             :      */
    1108                 :             :     a1len = add_size(add_size(len1, len1), 2);
    1109                 :             :     a2len = add_size(add_size(len2, len2), 2);
    1110                 :             :     buflen = add_size(a1len, a2len);
    1111                 :             : 
    1112                 :             :     if (buflen > TEXTBUFLEN)
    1113                 :             :         buf = palloc(buflen);
    1114                 :             : 
    1115                 :             :     a1p = buf;
    1116                 :             :     a2p = buf + a1len;
    1117                 :             : 
    1118                 :             :     /* API does not work for zero-length input */
    1119                 :             :     if (len1 == 0)
    1120                 :             :         r = 0;
    1121                 :             :     else
    1122                 :             :     {
    1123                 :             :         r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
    1124                 :             :                                 (LPWSTR) a1p, a1len / 2);
    1125                 :             :         if (!r)
    1126                 :             :             ereport(ERROR,
    1127                 :             :                     (errmsg("could not convert string to UTF-16: error code %lu",
    1128                 :             :                             GetLastError())));
    1129                 :             :     }
    1130                 :             :     ((LPWSTR) a1p)[r] = 0;
    1131                 :             : 
    1132                 :             :     if (len2 == 0)
    1133                 :             :         r = 0;
    1134                 :             :     else
    1135                 :             :     {
    1136                 :             :         r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
    1137                 :             :                                 (LPWSTR) a2p, a2len / 2);
    1138                 :             :         if (!r)
    1139                 :             :             ereport(ERROR,
    1140                 :             :                     (errmsg("could not convert string to UTF-16: error code %lu",
    1141                 :             :                             GetLastError())));
    1142                 :             :     }
    1143                 :             :     ((LPWSTR) a2p)[r] = 0;
    1144                 :             : 
    1145                 :             :     errno = 0;
    1146                 :             :     result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->lt);
    1147                 :             :     if (result == 2147483647)   /* _NLSCMPERROR; missing from mingw headers */
    1148                 :             :         ereport(ERROR,
    1149                 :             :                 (errmsg("could not compare Unicode strings: %m")));
    1150                 :             : 
    1151                 :             :     if (buf != sbuf)
    1152                 :             :         pfree(buf);
    1153                 :             : 
    1154                 :             :     return result;
    1155                 :             : }
    1156                 :             : 
    1157                 :             : static int
    1158                 :             : strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
    1159                 :             :                         pg_locale_t locale)
    1160                 :             : {
    1161                 :             :     size_t      len1 = strlen(arg1);
    1162                 :             :     size_t      len2 = strlen(arg2);
    1163                 :             : 
    1164                 :             :     return strncoll_libc_win32_utf8(arg1, len1, arg2, len2, locale);
    1165                 :             : }
    1166                 :             : #endif                          /* WIN32 */
    1167                 :             : 
    1168                 :             : /* simple subroutine for reporting errors from newlocale() */
    1169                 :             : void
    1170                 :           0 : report_newlocale_failure(const char *localename)
    1171                 :             : {
    1172                 :             :     int         save_errno;
    1173                 :             : 
    1174                 :             :     /*
    1175                 :             :      * Windows doesn't provide any useful error indication from
    1176                 :             :      * _create_locale(), and BSD-derived platforms don't seem to feel they
    1177                 :             :      * need to set errno either (even though POSIX is pretty clear that
    1178                 :             :      * newlocale should do so).  So, if errno hasn't been set, assume ENOENT
    1179                 :             :      * is what to report.
    1180                 :             :      */
    1181         [ #  # ]:           0 :     if (errno == 0)
    1182                 :           0 :         errno = ENOENT;
    1183                 :             : 
    1184                 :             :     /*
    1185                 :             :      * ENOENT means "no such locale", not "no such file", so clarify that
    1186                 :             :      * errno with an errdetail message.
    1187                 :             :      */
    1188                 :           0 :     save_errno = errno;         /* auxiliary funcs might change errno */
    1189   [ #  #  #  # ]:           0 :     ereport(ERROR,
    1190                 :             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1191                 :             :              errmsg("could not create locale \"%s\": %m",
    1192                 :             :                     localename),
    1193                 :             :              (save_errno == ENOENT ?
    1194                 :             :               errdetail("The operating system could not find any locale data for the locale name \"%s\".",
    1195                 :             :                         localename) : 0)));
    1196                 :             : }
    1197                 :             : 
    1198                 :             : /*
    1199                 :             :  * POSIX doesn't define _l-variants of these functions, but several systems
    1200                 :             :  * have them.  We provide our own replacements here.
    1201                 :             :  */
    1202                 :             : #ifndef HAVE_MBSTOWCS_L
    1203                 :             : static size_t
    1204                 :     1044490 : mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
    1205                 :             : {
    1206                 :             : #ifdef WIN32
    1207                 :             :     return _mbstowcs_l(dest, src, n, loc);
    1208                 :             : #else
    1209                 :             :     size_t      result;
    1210                 :     1044490 :     locale_t    save_locale = uselocale(loc);
    1211                 :             : 
    1212                 :     1044490 :     result = mbstowcs(dest, src, n);
    1213                 :     1044490 :     uselocale(save_locale);
    1214                 :     1044490 :     return result;
    1215                 :             : #endif
    1216                 :             : }
    1217                 :             : #endif
    1218                 :             : #ifndef HAVE_WCSTOMBS_L
    1219                 :             : static size_t
    1220                 :     1044490 : wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
    1221                 :             : {
    1222                 :             : #ifdef WIN32
    1223                 :             :     return _wcstombs_l(dest, src, n, loc);
    1224                 :             : #else
    1225                 :             :     size_t      result;
    1226                 :     1044490 :     locale_t    save_locale = uselocale(loc);
    1227                 :             : 
    1228                 :     1044490 :     result = wcstombs(dest, src, n);
    1229                 :     1044490 :     uselocale(save_locale);
    1230                 :     1044490 :     return result;
    1231                 :             : #endif
    1232                 :             : }
    1233                 :             : #endif
    1234                 :             : 
    1235                 :             : /*
    1236                 :             :  * These functions convert from/to libc's wchar_t, *not* pg_wchar.
    1237                 :             :  * Therefore we keep them here rather than with the mbutils code.
    1238                 :             :  */
    1239                 :             : 
    1240                 :             : /*
    1241                 :             :  * wchar2char --- convert wide characters to multibyte format
    1242                 :             :  *
    1243                 :             :  * This has the same API as the standard wcstombs_l() function; in particular,
    1244                 :             :  * tolen is the maximum number of bytes to store at *to, and *from must be
    1245                 :             :  * zero-terminated.  The output will be zero-terminated iff there is room.
    1246                 :             :  */
    1247                 :             : size_t
    1248                 :     1044490 : wchar2char(char *to, const wchar_t *from, size_t tolen, locale_t loc)
    1249                 :             : {
    1250                 :             :     size_t      result;
    1251                 :             : 
    1252         [ -  + ]:     1044490 :     if (tolen == 0)
    1253                 :           0 :         return 0;
    1254                 :             : 
    1255                 :             : #ifdef WIN32
    1256                 :             : 
    1257                 :             :     /*
    1258                 :             :      * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding, and
    1259                 :             :      * for some reason mbstowcs and wcstombs won't do this for us, so we use
    1260                 :             :      * MultiByteToWideChar().
    1261                 :             :      */
    1262                 :             :     if (GetDatabaseEncoding() == PG_UTF8)
    1263                 :             :     {
    1264                 :             :         result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
    1265                 :             :                                      NULL, NULL);
    1266                 :             :         /* A zero return is failure */
    1267                 :             :         if (result <= 0)
    1268                 :             :             result = -1;
    1269                 :             :         else
    1270                 :             :         {
    1271                 :             :             Assert(result <= tolen);
    1272                 :             :             /* Microsoft counts the zero terminator in the result */
    1273                 :             :             result--;
    1274                 :             :         }
    1275                 :             :     }
    1276                 :             :     else
    1277                 :             : #endif                          /* WIN32 */
    1278         [ -  + ]:     1044490 :     if (loc == (locale_t) 0)
    1279                 :             :     {
    1280                 :             :         /* Use wcstombs directly for the default locale */
    1281                 :           0 :         result = wcstombs(to, from, tolen);
    1282                 :             :     }
    1283                 :             :     else
    1284                 :             :     {
    1285                 :             :         /* Use wcstombs_l for nondefault locales */
    1286                 :     1044490 :         result = wcstombs_l(to, from, tolen, loc);
    1287                 :             :     }
    1288                 :             : 
    1289                 :     1044490 :     return result;
    1290                 :             : }
    1291                 :             : 
    1292                 :             : /*
    1293                 :             :  * char2wchar --- convert multibyte characters to wide characters
    1294                 :             :  *
    1295                 :             :  * This has almost the API of mbstowcs_l(), except that *from need not be
    1296                 :             :  * null-terminated; instead, the number of input bytes is specified as
    1297                 :             :  * fromlen.  Also, we ereport() rather than returning -1 for invalid
    1298                 :             :  * input encoding.  tolen is the maximum number of wchar_t's to store at *to.
    1299                 :             :  * The output will be zero-terminated iff there is room.
    1300                 :             :  */
    1301                 :             : static size_t
    1302                 :     1044490 : char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
    1303                 :             :            locale_t loc)
    1304                 :             : {
    1305                 :             :     size_t      result;
    1306                 :             : 
    1307         [ -  + ]:     1044490 :     if (tolen == 0)
    1308                 :           0 :         return 0;
    1309                 :             : 
    1310                 :             : #ifdef WIN32
    1311                 :             :     /* See WIN32 "Unicode" comment above */
    1312                 :             :     if (GetDatabaseEncoding() == PG_UTF8)
    1313                 :             :     {
    1314                 :             :         /* Win32 API does not work for zero-length input */
    1315                 :             :         if (fromlen == 0)
    1316                 :             :             result = 0;
    1317                 :             :         else
    1318                 :             :         {
    1319                 :             :             result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
    1320                 :             :             /* A zero return is failure */
    1321                 :             :             if (result == 0)
    1322                 :             :                 result = -1;
    1323                 :             :         }
    1324                 :             : 
    1325                 :             :         if (result != -1)
    1326                 :             :         {
    1327                 :             :             Assert(result < tolen);
    1328                 :             :             /* Append trailing null wchar (MultiByteToWideChar() does not) */
    1329                 :             :             to[result] = 0;
    1330                 :             :         }
    1331                 :             :     }
    1332                 :             :     else
    1333                 :             : #endif                          /* WIN32 */
    1334                 :             :     {
    1335                 :             :         /* mbstowcs requires ending '\0' */
    1336                 :     1044490 :         char       *str = pnstrdup(from, fromlen);
    1337                 :             : 
    1338         [ -  + ]:     1044490 :         if (loc == (locale_t) 0)
    1339                 :             :         {
    1340                 :             :             /* Use mbstowcs directly for the default locale */
    1341                 :           0 :             result = mbstowcs(to, str, tolen);
    1342                 :             :         }
    1343                 :             :         else
    1344                 :             :         {
    1345                 :             :             /* Use mbstowcs_l for nondefault locales */
    1346                 :     1044490 :             result = mbstowcs_l(to, str, tolen, loc);
    1347                 :             :         }
    1348                 :             : 
    1349                 :     1044490 :         pfree(str);
    1350                 :             :     }
    1351                 :             : 
    1352         [ -  + ]:     1044490 :     if (result == -1)
    1353                 :             :     {
    1354                 :             :         /*
    1355                 :             :          * Invalid multibyte character encountered.  We try to give a useful
    1356                 :             :          * error message by letting pg_verifymbstr check the string.  But it's
    1357                 :             :          * possible that the string is OK to us, and not OK to mbstowcs ---
    1358                 :             :          * this suggests that the LC_CTYPE locale is different from the
    1359                 :             :          * database encoding.  Give a generic error message if pg_verifymbstr
    1360                 :             :          * can't find anything wrong.
    1361                 :             :          */
    1362                 :           0 :         pg_verifymbstr(from, fromlen, false);   /* might not return */
    1363                 :             :         /* but if it does ... */
    1364         [ #  # ]:           0 :         ereport(ERROR,
    1365                 :             :                 (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
    1366                 :             :                  errmsg("invalid multibyte character for locale"),
    1367                 :             :                  errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
    1368                 :             :     }
    1369                 :             : 
    1370                 :     1044490 :     return result;
    1371                 :             : }
        

Generated by: LCOV version 2.0-1