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
DefineCollation(ParseState * pstate,List * names,List * parameters,bool if_not_exists)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, ACL_KIND_NAMESPACE,
78 get_namespace_name(collNamespace));
79
80 foreach(pl, parameters)
81 {
82 DefElem *defel = lfirst_node(DefElem, pl);
83 DefElem **defelp;
84
85 if (pg_strcasecmp(defel->defname, "from") == 0)
86 defelp = &fromEl;
87 else if (pg_strcasecmp(defel->defname, "locale") == 0)
88 defelp = &localeEl;
89 else if (pg_strcasecmp(defel->defname, "lc_collate") == 0)
90 defelp = &lccollateEl;
91 else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0)
92 defelp = &lcctypeEl;
93 else if (pg_strcasecmp(defel->defname, "provider") == 0)
94 defelp = &providerEl;
95 else if (pg_strcasecmp(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 {
193 #ifdef USE_ICU
194 /*
195 * We could create ICU collations with collencoding == database
196 * encoding, but it seems better to use -1 so that it matches the
197 * way initdb would create ICU collations. However, only allow
198 * one to be created when the current database's encoding is
199 * supported. Otherwise the collation is useless, plus we get
200 * surprising behaviors like not being able to drop the collation.
201 *
202 * Skip this test when !USE_ICU, because the error we want to
203 * throw for that isn't thrown till later.
204 */
205 if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
206 ereport(ERROR,
207 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
208 errmsg("current database's encoding is not supported with this provider")));
209 #endif
210 collencoding = -1;
211 }
212 else
213 {
214 collencoding = GetDatabaseEncoding();
215 check_encoding_locale_matches(collencoding, collcollate, collctype);
216 }
217 }
218
219 if (!collversion)
220 collversion = get_collation_actual_version(collprovider, collcollate);
221
222 newoid = CollationCreate(collName,
223 collNamespace,
224 GetUserId(),
225 collprovider,
226 collencoding,
227 collcollate,
228 collctype,
229 collversion,
230 if_not_exists,
231 false); /* not quiet */
232
233 if (!OidIsValid(newoid))
234 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 CommandCounterIncrement();
241 if (!lc_collate_is_c(newoid) || !lc_ctype_is_c(newoid))
242 (void) pg_newlocale_from_collation(newoid);
243
244 ObjectAddressSet(address, CollationRelationId, newoid);
245
246 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
IsThereCollationInNamespace(const char * collname,Oid nspOid)256 IsThereCollationInNamespace(const char *collname, Oid nspOid)
257 {
258 /* make sure the name doesn't already exist in new schema */
259 if (SearchSysCacheExists3(COLLNAMEENCNSP,
260 CStringGetDatum(collname),
261 Int32GetDatum(GetDatabaseEncoding()),
262 ObjectIdGetDatum(nspOid)))
263 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 if (SearchSysCacheExists3(COLLNAMEENCNSP,
271 CStringGetDatum(collname),
272 Int32GetDatum(-1),
273 ObjectIdGetDatum(nspOid)))
274 ereport(ERROR,
275 (errcode(ERRCODE_DUPLICATE_OBJECT),
276 errmsg("collation \"%s\" already exists in schema \"%s\"",
277 collname, get_namespace_name(nspOid))));
278 }
279
280 /*
281 * ALTER COLLATION
282 */
283 ObjectAddress
AlterCollation(AlterCollationStmt * stmt)284 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 rel = heap_open(CollationRelationId, RowExclusiveLock);
297 collOid = get_collation_oid(stmt->collname, false);
298
299 if (!pg_collation_ownercheck(collOid, GetUserId()))
300 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION,
301 NameListToString(stmt->collname));
302
303 tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collOid));
304 if (!HeapTupleIsValid(tup))
305 elog(ERROR, "cache lookup failed for collation %u", collOid);
306
307 collForm = (Form_pg_collation) GETSTRUCT(tup);
308 collversion = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion,
309 &isnull);
310 oldversion = isnull ? NULL : TextDatumGetCString(collversion);
311
312 newversion = get_collation_actual_version(collForm->collprovider, NameStr(collForm->collcollate));
313
314 /* cannot change from NULL to non-NULL or vice versa */
315 if ((!oldversion && newversion) || (oldversion && !newversion))
316 elog(ERROR, "invalid collation version change");
317 else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
318 {
319 bool nulls[Natts_pg_collation];
320 bool replaces[Natts_pg_collation];
321 Datum values[Natts_pg_collation];
322
323 ereport(NOTICE,
324 (errmsg("changing version from %s to %s",
325 oldversion, newversion)));
326
327 memset(values, 0, sizeof(values));
328 memset(nulls, false, sizeof(nulls));
329 memset(replaces, false, sizeof(replaces));
330
331 values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
332 replaces[Anum_pg_collation_collversion - 1] = true;
333
334 tup = heap_modify_tuple(tup, RelationGetDescr(rel),
335 values, nulls, replaces);
336 }
337 else
338 ereport(NOTICE,
339 (errmsg("version has not changed")));
340
341 CatalogTupleUpdate(rel, &tup->t_self, tup);
342
343 InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
344
345 ObjectAddressSet(address, CollationRelationId, collOid);
346
347 heap_freetuple(tup);
348 heap_close(rel, NoLock);
349
350 return address;
351 }
352
353
354 Datum
pg_collation_actual_version(PG_FUNCTION_ARGS)355 pg_collation_actual_version(PG_FUNCTION_ARGS)
356 {
357 Oid collid = PG_GETARG_OID(0);
358 HeapTuple tp;
359 char *collcollate;
360 char collprovider;
361 char *version;
362
363 tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
364 if (!HeapTupleIsValid(tp))
365 ereport(ERROR,
366 (errcode(ERRCODE_UNDEFINED_OBJECT),
367 errmsg("collation with OID %u does not exist", collid)));
368
369 collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
370 collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
371
372 ReleaseSysCache(tp);
373
374 version = get_collation_actual_version(collprovider, collcollate);
375
376 if (version)
377 PG_RETURN_TEXT_P(cstring_to_text(version));
378 else
379 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
is_all_ascii(const char * str)393 is_all_ascii(const char *str)
394 {
395 while (*str)
396 {
397 if (IS_HIGHBIT_SET(*str))
398 return false;
399 str++;
400 }
401 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
normalize_libc_locale_name(char * new,const char * old)413 normalize_libc_locale_name(char *new, const char *old)
414 {
415 char *n = new;
416 const char *o = old;
417 bool changed = false;
418
419 while (*o)
420 {
421 if (*o == '.')
422 {
423 /* skip over encoding tag such as ".utf8" or ".UTF-8" */
424 o++;
425 while ((*o >= 'A' && *o <= 'Z')
426 || (*o >= 'a' && *o <= 'z')
427 || (*o >= '0' && *o <= '9')
428 || (*o == '-'))
429 o++;
430 changed = true;
431 }
432 else
433 *n++ = *o++;
434 }
435 *n = '\0';
436
437 return changed;
438 }
439
440 /*
441 * qsort comparator for CollAliasData items
442 */
443 static int
cmpaliases(const void * a,const void * b)444 cmpaliases(const void *a, const void *b)
445 {
446 const CollAliasData *ca = (const CollAliasData *) a;
447 const CollAliasData *cb = (const CollAliasData *) b;
448
449 /* comparing localename is enough because other fields are derived */
450 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 *
get_icu_language_tag(const char * localename)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 *
get_icu_locale_comment(const char * localename)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
pg_import_system_collations(PG_FUNCTION_ARGS)520 pg_import_system_collations(PG_FUNCTION_ARGS)
521 {
522 Oid nspid = PG_GETARG_OID(0);
523 int ncreated = 0;
524
525 if (!superuser())
526 ereport(ERROR,
527 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
528 (errmsg("must be superuser to import system collations"))));
529
530 if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(nspid)))
531 ereport(ERROR,
532 (errcode(ERRCODE_UNDEFINED_SCHEMA),
533 errmsg("schema with OID %u does not exist", nspid)));
534
535 /* Load collations known to libc, using "locale -a" to enumerate them */
536 #ifdef READ_LOCALE_A_OUTPUT
537 {
538 FILE *locale_a_handle;
539 char localebuf[NAMEDATALEN]; /* we assume ASCII so this is fine */
540 int nvalid = 0;
541 Oid collid;
542 CollAliasData *aliases;
543 int naliases,
544 maxaliases,
545 i;
546
547 /* expansible array of aliases */
548 maxaliases = 100;
549 aliases = (CollAliasData *) palloc(maxaliases * sizeof(CollAliasData));
550 naliases = 0;
551
552 locale_a_handle = OpenPipeStream("locale -a", "r");
553 if (locale_a_handle == NULL)
554 ereport(ERROR,
555 (errcode_for_file_access(),
556 errmsg("could not execute command \"%s\": %m",
557 "locale -a")));
558
559 while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
560 {
561 size_t len;
562 int enc;
563 char alias[NAMEDATALEN];
564
565 len = strlen(localebuf);
566
567 if (len == 0 || localebuf[len - 1] != '\n')
568 {
569 elog(DEBUG1, "locale name too long, skipped: \"%s\"", localebuf);
570 continue;
571 }
572 localebuf[len - 1] = '\0';
573
574 /*
575 * Some systems have locale names that don't consist entirely of
576 * ASCII letters (such as "bokmål" or "français").
577 * This is pretty silly, since we need the locale itself to
578 * interpret the non-ASCII characters. We can't do much with
579 * those, so we filter them out.
580 */
581 if (!is_all_ascii(localebuf))
582 {
583 elog(DEBUG1, "locale name has non-ASCII characters, skipped: \"%s\"", localebuf);
584 continue;
585 }
586
587 enc = pg_get_encoding_from_locale(localebuf, false);
588 if (enc < 0)
589 {
590 /* error message printed by pg_get_encoding_from_locale() */
591 continue;
592 }
593 if (!PG_VALID_BE_ENCODING(enc))
594 continue; /* ignore locales for client-only encodings */
595 if (enc == PG_SQL_ASCII)
596 continue; /* C/POSIX are already in the catalog */
597
598 /* count valid locales found in operating system */
599 nvalid++;
600
601 /*
602 * Create a collation named the same as the locale, but quietly
603 * doing nothing if it already exists. This is the behavior we
604 * need even at initdb time, because some versions of "locale -a"
605 * can report the same locale name more than once. And it's
606 * convenient for later import runs, too, since you just about
607 * always want to add on new locales without a lot of chatter
608 * about existing ones.
609 */
610 collid = CollationCreate(localebuf, nspid, GetUserId(),
611 COLLPROVIDER_LIBC, enc,
612 localebuf, localebuf,
613 get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
614 true, true);
615 if (OidIsValid(collid))
616 {
617 ncreated++;
618
619 /* Must do CCI between inserts to handle duplicates correctly */
620 CommandCounterIncrement();
621 }
622
623 /*
624 * Generate aliases such as "en_US" in addition to "en_US.utf8"
625 * for ease of use. Note that collation names are unique per
626 * encoding only, so this doesn't clash with "en_US" for LATIN1,
627 * say.
628 *
629 * However, it might conflict with a name we'll see later in the
630 * "locale -a" output. So save up the aliases and try to add them
631 * after we've read all the output.
632 */
633 if (normalize_libc_locale_name(alias, localebuf))
634 {
635 if (naliases >= maxaliases)
636 {
637 maxaliases *= 2;
638 aliases = (CollAliasData *)
639 repalloc(aliases, maxaliases * sizeof(CollAliasData));
640 }
641 aliases[naliases].localename = pstrdup(localebuf);
642 aliases[naliases].alias = pstrdup(alias);
643 aliases[naliases].enc = enc;
644 naliases++;
645 }
646 }
647
648 ClosePipeStream(locale_a_handle);
649
650 /*
651 * Before processing the aliases, sort them by locale name. The point
652 * here is that if "locale -a" gives us multiple locale names with the
653 * same encoding and base name, say "en_US.utf8" and "en_US.utf-8", we
654 * want to pick a deterministic one of them. First in ASCII sort
655 * order is a good enough rule. (Before PG 10, the code corresponding
656 * to this logic in initdb.c had an additional ordering rule, to
657 * prefer the locale name exactly matching the alias, if any. We
658 * don't need to consider that here, because we would have already
659 * created such a pg_collation entry above, and that one will win.)
660 */
661 if (naliases > 1)
662 qsort((void *) aliases, naliases, sizeof(CollAliasData), cmpaliases);
663
664 /* Now add aliases, ignoring any that match pre-existing entries */
665 for (i = 0; i < naliases; i++)
666 {
667 char *locale = aliases[i].localename;
668 char *alias = aliases[i].alias;
669 int enc = aliases[i].enc;
670
671 collid = CollationCreate(alias, nspid, GetUserId(),
672 COLLPROVIDER_LIBC, enc,
673 locale, locale,
674 get_collation_actual_version(COLLPROVIDER_LIBC, locale),
675 true, true);
676 if (OidIsValid(collid))
677 {
678 ncreated++;
679
680 CommandCounterIncrement();
681 }
682 }
683
684 /* Give a warning if "locale -a" seems to be malfunctioning */
685 if (nvalid == 0)
686 ereport(WARNING,
687 (errmsg("no usable system locales were found")));
688 }
689 #endif /* READ_LOCALE_A_OUTPUT */
690
691 /*
692 * Load collations known to ICU
693 *
694 * We use uloc_countAvailable()/uloc_getAvailable() rather than
695 * ucol_countAvailable()/ucol_getAvailable(). The former returns a full
696 * set of language+region combinations, whereas the latter only returns
697 * language+region combinations of they are distinct from the language's
698 * base collation. So there might not be a de-DE or en-GB, which would be
699 * confusing.
700 */
701 #ifdef USE_ICU
702 {
703 int i;
704
705 /*
706 * Start the loop at -1 to sneak in the root locale without too much
707 * code duplication.
708 */
709 for (i = -1; i < uloc_countAvailable(); i++)
710 {
711 const char *name;
712 char *langtag;
713 char *icucomment;
714 const char *collcollate;
715 Oid collid;
716
717 if (i == -1)
718 name = ""; /* ICU root locale */
719 else
720 name = uloc_getAvailable(i);
721
722 langtag = get_icu_language_tag(name);
723 collcollate = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : name;
724
725 /*
726 * Be paranoid about not allowing any non-ASCII strings into
727 * pg_collation
728 */
729 if (!is_all_ascii(langtag) || !is_all_ascii(collcollate))
730 continue;
731
732 collid = CollationCreate(psprintf("%s-x-icu", langtag),
733 nspid, GetUserId(),
734 COLLPROVIDER_ICU, -1,
735 collcollate, collcollate,
736 get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
737 true, true);
738 if (OidIsValid(collid))
739 {
740 ncreated++;
741
742 CommandCounterIncrement();
743
744 icucomment = get_icu_locale_comment(name);
745 if (icucomment)
746 CreateComments(collid, CollationRelationId, 0,
747 icucomment);
748 }
749 }
750 }
751 #endif /* USE_ICU */
752
753 PG_RETURN_INT32(ncreated);
754 }
755