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&aring;l" or "fran&ccedil;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