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