]> granicus.if.org Git - postgresql/blob - src/backend/commands/collationcmds.c
Avoid unnecessary use of pg_strcasecmp for already-downcased identifiers.
[postgresql] / src / backend / commands / collationcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * collationcmds.c
4  *        collation-related commands support code
5  *
6  * Portions Copyright (c) 1996-2018, 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 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         DefElem    *fromEl = NULL;
59         DefElem    *localeEl = NULL;
60         DefElem    *lccollateEl = NULL;
61         DefElem    *lcctypeEl = NULL;
62         DefElem    *providerEl = NULL;
63         DefElem    *versionEl = NULL;
64         char       *collcollate = NULL;
65         char       *collctype = NULL;
66         char       *collproviderstr = NULL;
67         int                     collencoding = 0;
68         char            collprovider = 0;
69         char       *collversion = NULL;
70         Oid                     newoid;
71         ObjectAddress address;
72
73         collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
74
75         aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE);
76         if (aclresult != ACLCHECK_OK)
77                 aclcheck_error(aclresult, OBJECT_SCHEMA,
78                                            get_namespace_name(collNamespace));
79
80         foreach(pl, parameters)
81         {
82                 DefElem    *defel = lfirst_node(DefElem, pl);
83                 DefElem   **defelp;
84
85                 if (strcmp(defel->defname, "from") == 0)
86                         defelp = &fromEl;
87                 else if (strcmp(defel->defname, "locale") == 0)
88                         defelp = &localeEl;
89                 else if (strcmp(defel->defname, "lc_collate") == 0)
90                         defelp = &lccollateEl;
91                 else if (strcmp(defel->defname, "lc_ctype") == 0)
92                         defelp = &lcctypeEl;
93                 else if (strcmp(defel->defname, "provider") == 0)
94                         defelp = &providerEl;
95                 else if (strcmp(defel->defname, "version") == 0)
96                         defelp = &versionEl;
97                 else
98                 {
99                         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                 *defelp = defel;
108         }
109
110         if ((localeEl && (lccollateEl || lcctypeEl))
111                 || (fromEl && list_length(parameters) != 1))
112                 ereport(ERROR,
113                                 (errcode(ERRCODE_SYNTAX_ERROR),
114                                  errmsg("conflicting or redundant options")));
115
116         if (fromEl)
117         {
118                 Oid                     collid;
119                 HeapTuple       tp;
120
121                 collid = get_collation_oid(defGetQualifiedName(fromEl), false);
122                 tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
123                 if (!HeapTupleIsValid(tp))
124                         elog(ERROR, "cache lookup failed for collation %u", collid);
125
126                 collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
127                 collctype = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype));
128                 collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
129                 collencoding = ((Form_pg_collation) GETSTRUCT(tp))->collencoding;
130
131                 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                 if (collprovider == COLLPROVIDER_DEFAULT)
141                         ereport(ERROR,
142                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
143                                          errmsg("collation \"default\" cannot be copied")));
144         }
145
146         if (localeEl)
147         {
148                 collcollate = defGetString(localeEl);
149                 collctype = defGetString(localeEl);
150         }
151
152         if (lccollateEl)
153                 collcollate = defGetString(lccollateEl);
154
155         if (lcctypeEl)
156                 collctype = defGetString(lcctypeEl);
157
158         if (providerEl)
159                 collproviderstr = defGetString(providerEl);
160
161         if (versionEl)
162                 collversion = defGetString(versionEl);
163
164         if (collproviderstr)
165         {
166                 if (pg_strcasecmp(collproviderstr, "icu") == 0)
167                         collprovider = COLLPROVIDER_ICU;
168                 else if (pg_strcasecmp(collproviderstr, "libc") == 0)
169                         collprovider = COLLPROVIDER_LIBC;
170                 else
171                         ereport(ERROR,
172                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
173                                          errmsg("unrecognized collation provider: %s",
174                                                         collproviderstr)));
175         }
176         else if (!fromEl)
177                 collprovider = COLLPROVIDER_LIBC;
178
179         if (!collcollate)
180                 ereport(ERROR,
181                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
182                                  errmsg("parameter \"lc_collate\" must be specified")));
183
184         if (!collctype)
185                 ereport(ERROR,
186                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
187                                  errmsg("parameter \"lc_ctype\" must be specified")));
188
189         if (!fromEl)
190         {
191                 if (collprovider == COLLPROVIDER_ICU)
192                         collencoding = -1;
193                 else
194                 {
195                         collencoding = GetDatabaseEncoding();
196                         check_encoding_locale_matches(collencoding, collcollate, collctype);
197                 }
198         }
199
200         if (!collversion)
201                 collversion = get_collation_actual_version(collprovider, collcollate);
202
203         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         if (!OidIsValid(newoid))
215                 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         CommandCounterIncrement();
222         if (!lc_collate_is_c(newoid) || !lc_ctype_is_c(newoid))
223                 (void) pg_newlocale_from_collation(newoid);
224
225         ObjectAddressSet(address, CollationRelationId, newoid);
226
227         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 IsThereCollationInNamespace(const char *collname, Oid nspOid)
238 {
239         /* make sure the name doesn't already exist in new schema */
240         if (SearchSysCacheExists3(COLLNAMEENCNSP,
241                                                           CStringGetDatum(collname),
242                                                           Int32GetDatum(GetDatabaseEncoding()),
243                                                           ObjectIdGetDatum(nspOid)))
244                 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         if (SearchSysCacheExists3(COLLNAMEENCNSP,
252                                                           CStringGetDatum(collname),
253                                                           Int32GetDatum(-1),
254                                                           ObjectIdGetDatum(nspOid)))
255                 ereport(ERROR,
256                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
257                                  errmsg("collation \"%s\" already exists in schema \"%s\"",
258                                                 collname, get_namespace_name(nspOid))));
259 }
260
261 /*
262  * ALTER COLLATION
263  */
264 ObjectAddress
265 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         rel = heap_open(CollationRelationId, RowExclusiveLock);
278         collOid = get_collation_oid(stmt->collname, false);
279
280         if (!pg_collation_ownercheck(collOid, GetUserId()))
281                 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_COLLATION,
282                                            NameListToString(stmt->collname));
283
284         tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collOid));
285         if (!HeapTupleIsValid(tup))
286                 elog(ERROR, "cache lookup failed for collation %u", collOid);
287
288         collForm = (Form_pg_collation) GETSTRUCT(tup);
289         collversion = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion,
290                                                                   &isnull);
291         oldversion = isnull ? NULL : TextDatumGetCString(collversion);
292
293         newversion = get_collation_actual_version(collForm->collprovider, NameStr(collForm->collcollate));
294
295         /* cannot change from NULL to non-NULL or vice versa */
296         if ((!oldversion && newversion) || (oldversion && !newversion))
297                 elog(ERROR, "invalid collation version change");
298         else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
299         {
300                 bool            nulls[Natts_pg_collation];
301                 bool            replaces[Natts_pg_collation];
302                 Datum           values[Natts_pg_collation];
303
304                 ereport(NOTICE,
305                                 (errmsg("changing version from %s to %s",
306                                                 oldversion, newversion)));
307
308                 memset(values, 0, sizeof(values));
309                 memset(nulls, false, sizeof(nulls));
310                 memset(replaces, false, sizeof(replaces));
311
312                 values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
313                 replaces[Anum_pg_collation_collversion - 1] = true;
314
315                 tup = heap_modify_tuple(tup, RelationGetDescr(rel),
316                                                                 values, nulls, replaces);
317         }
318         else
319                 ereport(NOTICE,
320                                 (errmsg("version has not changed")));
321
322         CatalogTupleUpdate(rel, &tup->t_self, tup);
323
324         InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
325
326         ObjectAddressSet(address, CollationRelationId, collOid);
327
328         heap_freetuple(tup);
329         heap_close(rel, NoLock);
330
331         return address;
332 }
333
334
335 Datum
336 pg_collation_actual_version(PG_FUNCTION_ARGS)
337 {
338         Oid                     collid = PG_GETARG_OID(0);
339         HeapTuple       tp;
340         char       *collcollate;
341         char            collprovider;
342         char       *version;
343
344         tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
345         if (!HeapTupleIsValid(tp))
346                 ereport(ERROR,
347                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
348                                  errmsg("collation with OID %u does not exist", collid)));
349
350         collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
351         collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
352
353         ReleaseSysCache(tp);
354
355         version = get_collation_actual_version(collprovider, collcollate);
356
357         if (version)
358                 PG_RETURN_TEXT_P(cstring_to_text(version));
359         else
360                 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 is_all_ascii(const char *str)
375 {
376         while (*str)
377         {
378                 if (IS_HIGHBIT_SET(*str))
379                         return false;
380                 str++;
381         }
382         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 normalize_libc_locale_name(char *new, const char *old)
395 {
396         char       *n = new;
397         const char *o = old;
398         bool            changed = false;
399
400         while (*o)
401         {
402                 if (*o == '.')
403                 {
404                         /* skip over encoding tag such as ".utf8" or ".UTF-8" */
405                         o++;
406                         while ((*o >= 'A' && *o <= 'Z')
407                                    || (*o >= 'a' && *o <= 'z')
408                                    || (*o >= '0' && *o <= '9')
409                                    || (*o == '-'))
410                                 o++;
411                         changed = true;
412                 }
413                 else
414                         *n++ = *o++;
415         }
416         *n = '\0';
417
418         return changed;
419 }
420
421 /*
422  * qsort comparator for CollAliasData items
423  */
424 static int
425 cmpaliases(const void *a, const void *b)
426 {
427         const CollAliasData *ca = (const CollAliasData *) a;
428         const CollAliasData *cb = (const CollAliasData *) b;
429
430         /* comparing localename is enough because other fields are derived */
431         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 pg_import_system_collations(PG_FUNCTION_ARGS)
502 {
503         Oid                     nspid = PG_GETARG_OID(0);
504         int                     ncreated = 0;
505
506         /* silence compiler warning if we have no locale implementation at all */
507         (void) nspid;
508
509         if (!superuser())
510                 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                 int                     nvalid = 0;
520                 Oid                     collid;
521                 CollAliasData *aliases;
522                 int                     naliases,
523                                         maxaliases,
524                                         i;
525
526                 /* expansible array of aliases */
527                 maxaliases = 100;
528                 aliases = (CollAliasData *) palloc(maxaliases * sizeof(CollAliasData));
529                 naliases = 0;
530
531                 locale_a_handle = OpenPipeStream("locale -a", "r");
532                 if (locale_a_handle == NULL)
533                         ereport(ERROR,
534                                         (errcode_for_file_access(),
535                                          errmsg("could not execute command \"%s\": %m",
536                                                         "locale -a")));
537
538                 while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
539                 {
540                         size_t          len;
541                         int                     enc;
542                         char            alias[NAMEDATALEN];
543
544                         len = strlen(localebuf);
545
546                         if (len == 0 || localebuf[len - 1] != '\n')
547                         {
548                                 elog(DEBUG1, "locale name too long, skipped: \"%s\"", localebuf);
549                                 continue;
550                         }
551                         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                         if (!is_all_ascii(localebuf))
561                         {
562                                 elog(DEBUG1, "locale name has non-ASCII characters, skipped: \"%s\"", localebuf);
563                                 continue;
564                         }
565
566                         enc = pg_get_encoding_from_locale(localebuf, false);
567                         if (enc < 0)
568                         {
569                                 /* error message printed by pg_get_encoding_from_locale() */
570                                 continue;
571                         }
572                         if (!PG_VALID_BE_ENCODING(enc))
573                                 continue;               /* ignore locales for client-only encodings */
574                         if (enc == PG_SQL_ASCII)
575                                 continue;               /* C/POSIX are already in the catalog */
576
577                         /* count valid locales found in operating system */
578                         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                         collid = CollationCreate(localebuf, nspid, GetUserId(),
590                                                                          COLLPROVIDER_LIBC, enc,
591                                                                          localebuf, localebuf,
592                                                                          get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
593                                                                          true, true);
594                         if (OidIsValid(collid))
595                         {
596                                 ncreated++;
597
598                                 /* Must do CCI between inserts to handle duplicates correctly */
599                                 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                         if (normalize_libc_locale_name(alias, localebuf))
613                         {
614                                 if (naliases >= maxaliases)
615                                 {
616                                         maxaliases *= 2;
617                                         aliases = (CollAliasData *)
618                                                 repalloc(aliases, maxaliases * sizeof(CollAliasData));
619                                 }
620                                 aliases[naliases].localename = pstrdup(localebuf);
621                                 aliases[naliases].alias = pstrdup(alias);
622                                 aliases[naliases].enc = enc;
623                                 naliases++;
624                         }
625                 }
626
627                 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                 if (naliases > 1)
641                         qsort((void *) aliases, naliases, sizeof(CollAliasData), cmpaliases);
642
643                 /* Now add aliases, ignoring any that match pre-existing entries */
644                 for (i = 0; i < naliases; i++)
645                 {
646                         char       *locale = aliases[i].localename;
647                         char       *alias = aliases[i].alias;
648                         int                     enc = aliases[i].enc;
649
650                         collid = CollationCreate(alias, nspid, GetUserId(),
651                                                                          COLLPROVIDER_LIBC, enc,
652                                                                          locale, locale,
653                                                                          get_collation_actual_version(COLLPROVIDER_LIBC, locale),
654                                                                          true, true);
655                         if (OidIsValid(collid))
656                         {
657                                 ncreated++;
658
659                                 CommandCounterIncrement();
660                         }
661                 }
662
663                 /* Give a warning if "locale -a" seems to be malfunctioning */
664                 if (nvalid == 0)
665                         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         PG_RETURN_INT32(ncreated);
733 }