LCOV - code coverage report
Current view: top level - src/backend/commands - collationcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 144 228 63.2 %
Date: 2020-06-01 07:06:57 Functions: 5 8 62.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * collationcmds.c
       4             :  *    collation-related commands support code
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/collationcmds.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/htup_details.h"
      18             : #include "access/table.h"
      19             : #include "access/xact.h"
      20             : #include "catalog/dependency.h"
      21             : #include "catalog/indexing.h"
      22             : #include "catalog/namespace.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_collation.h"
      25             : #include "commands/alter.h"
      26             : #include "commands/collationcmds.h"
      27             : #include "commands/comment.h"
      28             : #include "commands/dbcommands.h"
      29             : #include "commands/defrem.h"
      30             : #include "mb/pg_wchar.h"
      31             : #include "miscadmin.h"
      32             : #include "utils/acl.h"
      33             : #include "utils/builtins.h"
      34             : #include "utils/lsyscache.h"
      35             : #include "utils/pg_locale.h"
      36             : #include "utils/rel.h"
      37             : #include "utils/syscache.h"
      38             : 
      39             : 
      40             : typedef struct
      41             : {
      42             :     char       *localename;     /* name of locale, as per "locale -a" */
      43             :     char       *alias;          /* shortened alias for same */
      44             :     int         enc;            /* encoding */
      45             : } CollAliasData;
      46             : 
      47             : 
      48             : /*
      49             :  * CREATE COLLATION
      50             :  */
      51             : ObjectAddress
      52          20 : DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
      53             : {
      54             :     char       *collName;
      55             :     Oid         collNamespace;
      56             :     AclResult   aclresult;
      57             :     ListCell   *pl;
      58          20 :     DefElem    *fromEl = NULL;
      59          20 :     DefElem    *localeEl = NULL;
      60          20 :     DefElem    *lccollateEl = NULL;
      61          20 :     DefElem    *lcctypeEl = NULL;
      62          20 :     DefElem    *providerEl = NULL;
      63          20 :     DefElem    *deterministicEl = NULL;
      64          20 :     DefElem    *versionEl = NULL;
      65          20 :     char       *collcollate = NULL;
      66          20 :     char       *collctype = NULL;
      67          20 :     char       *collproviderstr = NULL;
      68          20 :     bool        collisdeterministic = true;
      69          20 :     int         collencoding = 0;
      70          20 :     char        collprovider = 0;
      71          20 :     char       *collversion = NULL;
      72             :     Oid         newoid;
      73             :     ObjectAddress address;
      74             : 
      75          20 :     collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
      76             : 
      77          20 :     aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE);
      78          20 :     if (aclresult != ACLCHECK_OK)
      79           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
      80           0 :                        get_namespace_name(collNamespace));
      81             : 
      82          40 :     foreach(pl, parameters)
      83             :     {
      84          24 :         DefElem    *defel = lfirst_node(DefElem, pl);
      85             :         DefElem   **defelp;
      86             : 
      87          24 :         if (strcmp(defel->defname, "from") == 0)
      88          12 :             defelp = &fromEl;
      89          12 :         else if (strcmp(defel->defname, "locale") == 0)
      90           0 :             defelp = &localeEl;
      91          12 :         else if (strcmp(defel->defname, "lc_collate") == 0)
      92           4 :             defelp = &lccollateEl;
      93           8 :         else if (strcmp(defel->defname, "lc_ctype") == 0)
      94           4 :             defelp = &lcctypeEl;
      95           4 :         else if (strcmp(defel->defname, "provider") == 0)
      96           0 :             defelp = &providerEl;
      97           4 :         else if (strcmp(defel->defname, "deterministic") == 0)
      98           0 :             defelp = &deterministicEl;
      99           4 :         else if (strcmp(defel->defname, "version") == 0)
     100           0 :             defelp = &versionEl;
     101             :         else
     102             :         {
     103           4 :             ereport(ERROR,
     104             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     105             :                      errmsg("collation attribute \"%s\" not recognized",
     106             :                             defel->defname),
     107             :                      parser_errposition(pstate, defel->location)));
     108             :             break;
     109             :         }
     110             : 
     111          20 :         *defelp = defel;
     112             :     }
     113             : 
     114          16 :     if ((localeEl && (lccollateEl || lcctypeEl))
     115          16 :         || (fromEl && list_length(parameters) != 1))
     116           0 :         ereport(ERROR,
     117             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     118             :                  errmsg("conflicting or redundant options")));
     119             : 
     120          16 :     if (fromEl)
     121             :     {
     122             :         Oid         collid;
     123             :         HeapTuple   tp;
     124             : 
     125          12 :         collid = get_collation_oid(defGetQualifiedName(fromEl), false);
     126          12 :         tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
     127          12 :         if (!HeapTupleIsValid(tp))
     128           0 :             elog(ERROR, "cache lookup failed for collation %u", collid);
     129             : 
     130          12 :         collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
     131          12 :         collctype = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype));
     132          12 :         collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
     133          12 :         collisdeterministic = ((Form_pg_collation) GETSTRUCT(tp))->collisdeterministic;
     134          12 :         collencoding = ((Form_pg_collation) GETSTRUCT(tp))->collencoding;
     135             : 
     136          12 :         ReleaseSysCache(tp);
     137             : 
     138             :         /*
     139             :          * Copying the "default" collation is not allowed because most code
     140             :          * checks for DEFAULT_COLLATION_OID instead of COLLPROVIDER_DEFAULT,
     141             :          * and so having a second collation with COLLPROVIDER_DEFAULT would
     142             :          * not work and potentially confuse or crash some code.  This could be
     143             :          * fixed with some legwork.
     144             :          */
     145          12 :         if (collprovider == COLLPROVIDER_DEFAULT)
     146           4 :             ereport(ERROR,
     147             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     148             :                      errmsg("collation \"default\" cannot be copied")));
     149             :     }
     150             : 
     151          12 :     if (localeEl)
     152             :     {
     153           0 :         collcollate = defGetString(localeEl);
     154           0 :         collctype = defGetString(localeEl);
     155             :     }
     156             : 
     157          12 :     if (lccollateEl)
     158           4 :         collcollate = defGetString(lccollateEl);
     159             : 
     160          12 :     if (lcctypeEl)
     161           4 :         collctype = defGetString(lcctypeEl);
     162             : 
     163          12 :     if (providerEl)
     164           0 :         collproviderstr = defGetString(providerEl);
     165             : 
     166          12 :     if (deterministicEl)
     167           0 :         collisdeterministic = defGetBoolean(deterministicEl);
     168             : 
     169          12 :     if (versionEl)
     170           0 :         collversion = defGetString(versionEl);
     171             : 
     172          12 :     if (collproviderstr)
     173             :     {
     174           0 :         if (pg_strcasecmp(collproviderstr, "icu") == 0)
     175           0 :             collprovider = COLLPROVIDER_ICU;
     176           0 :         else if (pg_strcasecmp(collproviderstr, "libc") == 0)
     177           0 :             collprovider = COLLPROVIDER_LIBC;
     178             :         else
     179           0 :             ereport(ERROR,
     180             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     181             :                      errmsg("unrecognized collation provider: %s",
     182             :                             collproviderstr)));
     183             :     }
     184          12 :     else if (!fromEl)
     185           4 :         collprovider = COLLPROVIDER_LIBC;
     186             : 
     187          12 :     if (!collcollate)
     188           0 :         ereport(ERROR,
     189             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     190             :                  errmsg("parameter \"lc_collate\" must be specified")));
     191             : 
     192          12 :     if (!collctype)
     193           0 :         ereport(ERROR,
     194             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     195             :                  errmsg("parameter \"lc_ctype\" must be specified")));
     196             : 
     197             :     /*
     198             :      * Nondeterministic collations are currently only supported with ICU
     199             :      * because that's the only case where it can actually make a difference.
     200             :      * So we can save writing the code for the other providers.
     201             :      */
     202          12 :     if (!collisdeterministic && collprovider != COLLPROVIDER_ICU)
     203           0 :         ereport(ERROR,
     204             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     205             :                  errmsg("nondeterministic collations not supported with this provider")));
     206             : 
     207          12 :     if (!fromEl)
     208             :     {
     209           4 :         if (collprovider == COLLPROVIDER_ICU)
     210           0 :             collencoding = -1;
     211             :         else
     212             :         {
     213           4 :             collencoding = GetDatabaseEncoding();
     214           4 :             check_encoding_locale_matches(collencoding, collcollate, collctype);
     215             :         }
     216             :     }
     217             : 
     218          12 :     if (!collversion)
     219          12 :         collversion = get_collation_actual_version(collprovider, collcollate);
     220             : 
     221          12 :     newoid = CollationCreate(collName,
     222             :                              collNamespace,
     223             :                              GetUserId(),
     224             :                              collprovider,
     225             :                              collisdeterministic,
     226             :                              collencoding,
     227             :                              collcollate,
     228             :                              collctype,
     229             :                              collversion,
     230             :                              if_not_exists,
     231             :                              false);    /* not quiet */
     232             : 
     233          12 :     if (!OidIsValid(newoid))
     234           0 :         return InvalidObjectAddress;
     235             : 
     236             :     /*
     237             :      * Check that the locales can be loaded.  NB: pg_newlocale_from_collation
     238             :      * is only supposed to be called on non-C-equivalent locales.
     239             :      */
     240          12 :     CommandCounterIncrement();
     241          12 :     if (!lc_collate_is_c(newoid) || !lc_ctype_is_c(newoid))
     242           0 :         (void) pg_newlocale_from_collation(newoid);
     243             : 
     244          12 :     ObjectAddressSet(address, CollationRelationId, newoid);
     245             : 
     246          12 :     return address;
     247             : }
     248             : 
     249             : /*
     250             :  * Subroutine for ALTER COLLATION SET SCHEMA and RENAME
     251             :  *
     252             :  * Is there a collation with the same name of the given collation already in
     253             :  * the given namespace?  If so, raise an appropriate error message.
     254             :  */
     255             : void
     256           0 : IsThereCollationInNamespace(const char *collname, Oid nspOid)
     257             : {
     258             :     /* make sure the name doesn't already exist in new schema */
     259           0 :     if (SearchSysCacheExists3(COLLNAMEENCNSP,
     260             :                               CStringGetDatum(collname),
     261             :                               Int32GetDatum(GetDatabaseEncoding()),
     262             :                               ObjectIdGetDatum(nspOid)))
     263           0 :         ereport(ERROR,
     264             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     265             :                  errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
     266             :                         collname, GetDatabaseEncodingName(),
     267             :                         get_namespace_name(nspOid))));
     268             : 
     269             :     /* mustn't match an any-encoding entry, either */
     270           0 :     if (SearchSysCacheExists3(COLLNAMEENCNSP,
     271             :                               CStringGetDatum(collname),
     272             :                               Int32GetDatum(-1),
     273             :                               ObjectIdGetDatum(nspOid)))
     274           0 :         ereport(ERROR,
     275             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     276             :                  errmsg("collation \"%s\" already exists in schema \"%s\"",
     277             :                         collname, get_namespace_name(nspOid))));
     278           0 : }
     279             : 
     280             : /*
     281             :  * ALTER COLLATION
     282             :  */
     283             : ObjectAddress
     284           0 : AlterCollation(AlterCollationStmt *stmt)
     285             : {
     286             :     Relation    rel;
     287             :     Oid         collOid;
     288             :     HeapTuple   tup;
     289             :     Form_pg_collation collForm;
     290             :     Datum       collversion;
     291             :     bool        isnull;
     292             :     char       *oldversion;
     293             :     char       *newversion;
     294             :     ObjectAddress address;
     295             : 
     296           0 :     rel = table_open(CollationRelationId, RowExclusiveLock);
     297           0 :     collOid = get_collation_oid(stmt->collname, false);
     298             : 
     299           0 :     if (!pg_collation_ownercheck(collOid, GetUserId()))
     300           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_COLLATION,
     301           0 :                        NameListToString(stmt->collname));
     302             : 
     303           0 :     tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collOid));
     304           0 :     if (!HeapTupleIsValid(tup))
     305           0 :         elog(ERROR, "cache lookup failed for collation %u", collOid);
     306             : 
     307           0 :     collForm = (Form_pg_collation) GETSTRUCT(tup);
     308           0 :     collversion = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion,
     309             :                                   &isnull);
     310           0 :     oldversion = isnull ? NULL : TextDatumGetCString(collversion);
     311             : 
     312           0 :     newversion = get_collation_actual_version(collForm->collprovider, NameStr(collForm->collcollate));
     313             : 
     314             :     /* cannot change from NULL to non-NULL or vice versa */
     315           0 :     if ((!oldversion && newversion) || (oldversion && !newversion))
     316           0 :         elog(ERROR, "invalid collation version change");
     317           0 :     else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
     318           0 :     {
     319             :         bool        nulls[Natts_pg_collation];
     320             :         bool        replaces[Natts_pg_collation];
     321             :         Datum       values[Natts_pg_collation];
     322             : 
     323           0 :         ereport(NOTICE,
     324             :                 (errmsg("changing version from %s to %s",
     325             :                         oldversion, newversion)));
     326             : 
     327           0 :         memset(values, 0, sizeof(values));
     328           0 :         memset(nulls, false, sizeof(nulls));
     329           0 :         memset(replaces, false, sizeof(replaces));
     330             : 
     331           0 :         values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
     332           0 :         replaces[Anum_pg_collation_collversion - 1] = true;
     333             : 
     334           0 :         tup = heap_modify_tuple(tup, RelationGetDescr(rel),
     335             :                                 values, nulls, replaces);
     336             :     }
     337             :     else
     338           0 :         ereport(NOTICE,
     339             :                 (errmsg("version has not changed")));
     340             : 
     341           0 :     CatalogTupleUpdate(rel, &tup->t_self, tup);
     342             : 
     343           0 :     InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
     344             : 
     345           0 :     ObjectAddressSet(address, CollationRelationId, collOid);
     346             : 
     347           0 :     heap_freetuple(tup);
     348           0 :     table_close(rel, NoLock);
     349             : 
     350           0 :     return address;
     351             : }
     352             : 
     353             : 
     354             : Datum
     355           0 : pg_collation_actual_version(PG_FUNCTION_ARGS)
     356             : {
     357           0 :     Oid         collid = PG_GETARG_OID(0);
     358             :     HeapTuple   tp;
     359             :     char       *collcollate;
     360             :     char        collprovider;
     361             :     char       *version;
     362             : 
     363           0 :     tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
     364           0 :     if (!HeapTupleIsValid(tp))
     365           0 :         ereport(ERROR,
     366             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     367             :                  errmsg("collation with OID %u does not exist", collid)));
     368             : 
     369           0 :     collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
     370           0 :     collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
     371             : 
     372           0 :     ReleaseSysCache(tp);
     373             : 
     374           0 :     version = get_collation_actual_version(collprovider, collcollate);
     375             : 
     376           0 :     if (version)
     377           0 :         PG_RETURN_TEXT_P(cstring_to_text(version));
     378             :     else
     379           0 :         PG_RETURN_NULL();
     380             : }
     381             : 
     382             : 
     383             : /* will we use "locale -a" in pg_import_system_collations? */
     384             : #if defined(HAVE_LOCALE_T) && !defined(WIN32)
     385             : #define READ_LOCALE_A_OUTPUT
     386             : #endif
     387             : 
     388             : #if defined(READ_LOCALE_A_OUTPUT) || defined(USE_ICU)
     389             : /*
     390             :  * Check a string to see if it is pure ASCII
     391             :  */
     392             : static bool
     393        1432 : is_all_ascii(const char *str)
     394             : {
     395        9666 :     while (*str)
     396             :     {
     397        8234 :         if (IS_HIGHBIT_SET(*str))
     398           0 :             return false;
     399        8234 :         str++;
     400             :     }
     401        1432 :     return true;
     402             : }
     403             : #endif                          /* READ_LOCALE_A_OUTPUT || USE_ICU */
     404             : 
     405             : #ifdef READ_LOCALE_A_OUTPUT
     406             : /*
     407             :  * "Normalize" a libc locale name, stripping off encoding tags such as
     408             :  * ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
     409             :  * -> "br_FR@euro").  Return true if a new, different name was
     410             :  * generated.
     411             :  */
     412             : static bool
     413         716 : normalize_libc_locale_name(char *new, const char *old)
     414             : {
     415         716 :     char       *n = new;
     416         716 :     const char *o = old;
     417         716 :     bool        changed = false;
     418             : 
     419        3580 :     while (*o)
     420             :     {
     421        2864 :         if (*o == '.')
     422             :         {
     423             :             /* skip over encoding tag such as ".utf8" or ".UTF-8" */
     424         716 :             o++;
     425        3938 :             while ((*o >= 'A' && *o <= 'Z')
     426        2864 :                    || (*o >= 'a' && *o <= 'z')
     427        1790 :                    || (*o >= '0' && *o <= '9')
     428        1074 :                    || (*o == '-'))
     429        3222 :                 o++;
     430         716 :             changed = true;
     431             :         }
     432             :         else
     433        2148 :             *n++ = *o++;
     434             :     }
     435         716 :     *n = '\0';
     436             : 
     437         716 :     return changed;
     438             : }
     439             : 
     440             : /*
     441             :  * qsort comparator for CollAliasData items
     442             :  */
     443             : static int
     444         358 : cmpaliases(const void *a, const void *b)
     445             : {
     446         358 :     const CollAliasData *ca = (const CollAliasData *) a;
     447         358 :     const CollAliasData *cb = (const CollAliasData *) b;
     448             : 
     449             :     /* comparing localename is enough because other fields are derived */
     450         358 :     return strcmp(ca->localename, cb->localename);
     451             : }
     452             : #endif                          /* READ_LOCALE_A_OUTPUT */
     453             : 
     454             : 
     455             : #ifdef USE_ICU
     456             : /*
     457             :  * Get the ICU language tag for a locale name.
     458             :  * The result is a palloc'd string.
     459             :  */
     460             : static char *
     461             : get_icu_language_tag(const char *localename)
     462             : {
     463             :     char        buf[ULOC_FULLNAME_CAPACITY];
     464             :     UErrorCode  status;
     465             : 
     466             :     status = U_ZERO_ERROR;
     467             :     uloc_toLanguageTag(localename, buf, sizeof(buf), TRUE, &status);
     468             :     if (U_FAILURE(status))
     469             :         ereport(ERROR,
     470             :                 (errmsg("could not convert locale name \"%s\" to language tag: %s",
     471             :                         localename, u_errorName(status))));
     472             : 
     473             :     return pstrdup(buf);
     474             : }
     475             : 
     476             : /*
     477             :  * Get a comment (specifically, the display name) for an ICU locale.
     478             :  * The result is a palloc'd string, or NULL if we can't get a comment
     479             :  * or find that it's not all ASCII.  (We can *not* accept non-ASCII
     480             :  * comments, because the contents of template0 must be encoding-agnostic.)
     481             :  */
     482             : static char *
     483             : get_icu_locale_comment(const char *localename)
     484             : {
     485             :     UErrorCode  status;
     486             :     UChar       displayname[128];
     487             :     int32       len_uchar;
     488             :     int32       i;
     489             :     char       *result;
     490             : 
     491             :     status = U_ZERO_ERROR;
     492             :     len_uchar = uloc_getDisplayName(localename, "en",
     493             :                                     displayname, lengthof(displayname),
     494             :                                     &status);
     495             :     if (U_FAILURE(status))
     496             :         return NULL;            /* no good reason to raise an error */
     497             : 
     498             :     /* Check for non-ASCII comment (can't use is_all_ascii for this) */
     499             :     for (i = 0; i < len_uchar; i++)
     500             :     {
     501             :         if (displayname[i] > 127)
     502             :             return NULL;
     503             :     }
     504             : 
     505             :     /* OK, transcribe */
     506             :     result = palloc(len_uchar + 1);
     507             :     for (i = 0; i < len_uchar; i++)
     508             :         result[i] = displayname[i];
     509             :     result[len_uchar] = '\0';
     510             : 
     511             :     return result;
     512             : }
     513             : #endif                          /* USE_ICU */
     514             : 
     515             : 
     516             : /*
     517             :  * pg_import_system_collations: add known system collations to pg_collation
     518             :  */
     519             : Datum
     520         358 : pg_import_system_collations(PG_FUNCTION_ARGS)
     521             : {
     522         358 :     Oid         nspid = PG_GETARG_OID(0);
     523         358 :     int         ncreated = 0;
     524             : 
     525             :     /* silence compiler warning if we have no locale implementation at all */
     526             :     (void) nspid;
     527             : 
     528         358 :     if (!superuser())
     529           0 :         ereport(ERROR,
     530             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     531             :                  errmsg("must be superuser to import system collations")));
     532             : 
     533             :     /* Load collations known to libc, using "locale -a" to enumerate them */
     534             : #ifdef READ_LOCALE_A_OUTPUT
     535             :     {
     536             :         FILE       *locale_a_handle;
     537             :         char        localebuf[NAMEDATALEN]; /* we assume ASCII so this is fine */
     538         358 :         int         nvalid = 0;
     539             :         Oid         collid;
     540             :         CollAliasData *aliases;
     541             :         int         naliases,
     542             :                     maxaliases,
     543             :                     i;
     544             : 
     545             :         /* expansible array of aliases */
     546         358 :         maxaliases = 100;
     547         358 :         aliases = (CollAliasData *) palloc(maxaliases * sizeof(CollAliasData));
     548         358 :         naliases = 0;
     549             : 
     550         358 :         locale_a_handle = OpenPipeStream("locale -a", "r");
     551         358 :         if (locale_a_handle == NULL)
     552           0 :             ereport(ERROR,
     553             :                     (errcode_for_file_access(),
     554             :                      errmsg("could not execute command \"%s\": %m",
     555             :                             "locale -a")));
     556             : 
     557        1790 :         while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
     558             :         {
     559             :             size_t      len;
     560             :             int         enc;
     561             :             char        alias[NAMEDATALEN];
     562             : 
     563        1432 :             len = strlen(localebuf);
     564             : 
     565        1432 :             if (len == 0 || localebuf[len - 1] != '\n')
     566             :             {
     567           0 :                 elog(DEBUG1, "locale name too long, skipped: \"%s\"", localebuf);
     568         716 :                 continue;
     569             :             }
     570        1432 :             localebuf[len - 1] = '\0';
     571             : 
     572             :             /*
     573             :              * Some systems have locale names that don't consist entirely of
     574             :              * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais").
     575             :              * This is pretty silly, since we need the locale itself to
     576             :              * interpret the non-ASCII characters. We can't do much with
     577             :              * those, so we filter them out.
     578             :              */
     579        1432 :             if (!is_all_ascii(localebuf))
     580             :             {
     581           0 :                 elog(DEBUG1, "locale name has non-ASCII characters, skipped: \"%s\"", localebuf);
     582           0 :                 continue;
     583             :             }
     584             : 
     585        1432 :             enc = pg_get_encoding_from_locale(localebuf, false);
     586        1432 :             if (enc < 0)
     587             :             {
     588             :                 /* error message printed by pg_get_encoding_from_locale() */
     589           0 :                 continue;
     590             :             }
     591        1432 :             if (!PG_VALID_BE_ENCODING(enc))
     592           0 :                 continue;       /* ignore locales for client-only encodings */
     593        1432 :             if (enc == PG_SQL_ASCII)
     594         716 :                 continue;       /* C/POSIX are already in the catalog */
     595             : 
     596             :             /* count valid locales found in operating system */
     597         716 :             nvalid++;
     598             : 
     599             :             /*
     600             :              * Create a collation named the same as the locale, but quietly
     601             :              * doing nothing if it already exists.  This is the behavior we
     602             :              * need even at initdb time, because some versions of "locale -a"
     603             :              * can report the same locale name more than once.  And it's
     604             :              * convenient for later import runs, too, since you just about
     605             :              * always want to add on new locales without a lot of chatter
     606             :              * about existing ones.
     607             :              */
     608         716 :             collid = CollationCreate(localebuf, nspid, GetUserId(),
     609             :                                      COLLPROVIDER_LIBC, true, enc,
     610             :                                      localebuf, localebuf,
     611         716 :                                      get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
     612             :                                      true, true);
     613         716 :             if (OidIsValid(collid))
     614             :             {
     615         716 :                 ncreated++;
     616             : 
     617             :                 /* Must do CCI between inserts to handle duplicates correctly */
     618         716 :                 CommandCounterIncrement();
     619             :             }
     620             : 
     621             :             /*
     622             :              * Generate aliases such as "en_US" in addition to "en_US.utf8"
     623             :              * for ease of use.  Note that collation names are unique per
     624             :              * encoding only, so this doesn't clash with "en_US" for LATIN1,
     625             :              * say.
     626             :              *
     627             :              * However, it might conflict with a name we'll see later in the
     628             :              * "locale -a" output.  So save up the aliases and try to add them
     629             :              * after we've read all the output.
     630             :              */
     631         716 :             if (normalize_libc_locale_name(alias, localebuf))
     632             :             {
     633         716 :                 if (naliases >= maxaliases)
     634             :                 {
     635           0 :                     maxaliases *= 2;
     636             :                     aliases = (CollAliasData *)
     637           0 :                         repalloc(aliases, maxaliases * sizeof(CollAliasData));
     638             :                 }
     639         716 :                 aliases[naliases].localename = pstrdup(localebuf);
     640         716 :                 aliases[naliases].alias = pstrdup(alias);
     641         716 :                 aliases[naliases].enc = enc;
     642         716 :                 naliases++;
     643             :             }
     644             :         }
     645             : 
     646         358 :         ClosePipeStream(locale_a_handle);
     647             : 
     648             :         /*
     649             :          * Before processing the aliases, sort them by locale name.  The point
     650             :          * here is that if "locale -a" gives us multiple locale names with the
     651             :          * same encoding and base name, say "en_US.utf8" and "en_US.utf-8", we
     652             :          * want to pick a deterministic one of them.  First in ASCII sort
     653             :          * order is a good enough rule.  (Before PG 10, the code corresponding
     654             :          * to this logic in initdb.c had an additional ordering rule, to
     655             :          * prefer the locale name exactly matching the alias, if any.  We
     656             :          * don't need to consider that here, because we would have already
     657             :          * created such a pg_collation entry above, and that one will win.)
     658             :          */
     659         358 :         if (naliases > 1)
     660         358 :             qsort((void *) aliases, naliases, sizeof(CollAliasData), cmpaliases);
     661             : 
     662             :         /* Now add aliases, ignoring any that match pre-existing entries */
     663        1074 :         for (i = 0; i < naliases; i++)
     664             :         {
     665         716 :             char       *locale = aliases[i].localename;
     666         716 :             char       *alias = aliases[i].alias;
     667         716 :             int         enc = aliases[i].enc;
     668             : 
     669         716 :             collid = CollationCreate(alias, nspid, GetUserId(),
     670             :                                      COLLPROVIDER_LIBC, true, enc,
     671             :                                      locale, locale,
     672         716 :                                      get_collation_actual_version(COLLPROVIDER_LIBC, locale),
     673             :                                      true, true);
     674         716 :             if (OidIsValid(collid))
     675             :             {
     676         358 :                 ncreated++;
     677             : 
     678         358 :                 CommandCounterIncrement();
     679             :             }
     680             :         }
     681             : 
     682             :         /* Give a warning if "locale -a" seems to be malfunctioning */
     683         358 :         if (nvalid == 0)
     684           0 :             ereport(WARNING,
     685             :                     (errmsg("no usable system locales were found")));
     686             :     }
     687             : #endif                          /* READ_LOCALE_A_OUTPUT */
     688             : 
     689             :     /*
     690             :      * Load collations known to ICU
     691             :      *
     692             :      * We use uloc_countAvailable()/uloc_getAvailable() rather than
     693             :      * ucol_countAvailable()/ucol_getAvailable().  The former returns a full
     694             :      * set of language+region combinations, whereas the latter only returns
     695             :      * language+region combinations of they are distinct from the language's
     696             :      * base collation.  So there might not be a de-DE or en-GB, which would be
     697             :      * confusing.
     698             :      */
     699             : #ifdef USE_ICU
     700             :     {
     701             :         int         i;
     702             : 
     703             :         /*
     704             :          * Start the loop at -1 to sneak in the root locale without too much
     705             :          * code duplication.
     706             :          */
     707             :         for (i = -1; i < uloc_countAvailable(); i++)
     708             :         {
     709             :             const char *name;
     710             :             char       *langtag;
     711             :             char       *icucomment;
     712             :             const char *collcollate;
     713             :             Oid         collid;
     714             : 
     715             :             if (i == -1)
     716             :                 name = "";        /* ICU root locale */
     717             :             else
     718             :                 name = uloc_getAvailable(i);
     719             : 
     720             :             langtag = get_icu_language_tag(name);
     721             :             collcollate = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : name;
     722             : 
     723             :             /*
     724             :              * Be paranoid about not allowing any non-ASCII strings into
     725             :              * pg_collation
     726             :              */
     727             :             if (!is_all_ascii(langtag) || !is_all_ascii(collcollate))
     728             :                 continue;
     729             : 
     730             :             collid = CollationCreate(psprintf("%s-x-icu", langtag),
     731             :                                      nspid, GetUserId(),
     732             :                                      COLLPROVIDER_ICU, true, -1,
     733             :                                      collcollate, collcollate,
     734             :                                      get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
     735             :                                      true, true);
     736             :             if (OidIsValid(collid))
     737             :             {
     738             :                 ncreated++;
     739             : 
     740             :                 CommandCounterIncrement();
     741             : 
     742             :                 icucomment = get_icu_locale_comment(name);
     743             :                 if (icucomment)
     744             :                     CreateComments(collid, CollationRelationId, 0,
     745             :                                    icucomment);
     746             :             }
     747             :         }
     748             :     }
     749             : #endif                          /* USE_ICU */
     750             : 
     751         358 :     PG_RETURN_INT32(ncreated);
     752             : }

Generated by: LCOV version 1.13