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

Generated by: LCOV version 1.11