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ål" or "franç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