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