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