projects/aports·blob master · refs

892 lines · 17.7 KiB

1From: Jakub Jirutka <[email protected]>
2Date: Wed, 03 Aug 2022 20:40:33 +0200
3Subject: [PATCH] Hack to generate usable ICU-based collations with
4 icu-data-en
5
6This is a downstream patch for Alpine Linux, it should never be
7upstreamed in this form!
8
9When the PostgreSQL cluster is initialized (using initdb(1)) or the
10DB administrator calls `pg_import_system_collations()` directly, this
11function creates COLLATIONs in the system catalog (pg_collations).
12There are two types: libc-based and ICU-based. The latter are created
13based on *locales* (not collations) known to ICU, i.e. based on the ICU
14data installed at the time.
15
16collationcmds.c includes the following comment:
17> We use uloc_countAvailable()/uloc_getAvailable() rather than
18> ucol_countAvailable()/ucol_getAvailable(). The former returns a full
19> set of language+region combinations, whereas the latter only returns
20> language+region combinations if they are distinct from the language's
21> base collation. So there might not be a de-DE or en-GB, which would be
22> confusing.
23
24There's a problem with this approach: locales and collations are two
25different things. ICU data may include collation algorithms and data for
26all or some languages, but not locales (language + country/region).
27The collation data is small compared to locales. There are ~800 locales
28(combinations of language, country and variants), but only 98 collations.
29There's a mapping between collations and locales hidden somewhere in ICU
30data.
31
32Since full ICU data is very big (30 MiB), we have created a stripped down
33variant with only English locale (package icu-data-en, 2.6 MiB). It also
34includes a subset of 18 collations that cover hundreds of languages.
35
36When the cluster is initialized or `pg_import_system_collations()` is
37called directly and only icu-data-en (default) is installed, the user
38ends up with only und, en and en_GB ICU-based COLLATIONs. The user can
39create missing COLLATIONs manually, but this a) is not expected nor
40reasonable behaviour, b) it's not easy to find out for which locales
41there's a collation available for.
42
43I couldn't find any way how to list all language+country variants for the
44given collation. It can be constructed when we iterate over all locales,
45but this approach is useless when we don't have the locale data
46available... I should also note that the reverse lookup (locale ->
47collation) is not a problem for ICU when full locale data is stripped.
48
49So I ended up with a very ugly workaround: pre-generating a list of
50collation -> locale mapping and embedding it in the collationcmds.c
51source. Then we replace `uloc_countAvailable()`/`uloc_getAvailable()`
52with `ucol_countAvailable()` / `ucol_getAvailable()` to iterate over
53the collations instead of locales and lookup the locales in the
54pre-generated list.
55
56This data is quite stable, there's a very low risk of getting outdated in
57a way that would be a problem.
58
59`icu_coll_locales` has been generated using the following code:
60
61 #include <stdio.h>
62 #include <string.h>
63 #include <unicode/ucol.h>
64
65 // Copy-pasted from collationcmds.c.
66 static char *get_icu_language_tag(const char *localename) {
67 char buf[ULOC_FULLNAME_CAPACITY];
68 UErrorCode status = U_ZERO_ERROR;
69
70 uloc_toLanguageTag(localename, buf, sizeof(buf), true, &status);
71
72 if (U_FAILURE(status)) {
73 fprintf(stderr, "could not convert locale name \"%s\" to language tag: %s\n",
74 localename, u_errorName(status));
75 return strdup(localename);
76 }
77 return strdup(buf);
78 }
79
80 int main() {
81 UErrorCode status = U_ZERO_ERROR;
82
83 for (int i = 0; i < uloc_countAvailable(); i++) {
84 const char *locale = uloc_getAvailable(i);
85
86 UCollator *collator = ucol_open(locale, &status);
87 const char *actual_locale = ucol_getLocaleByType(collator, ULOC_ACTUAL_LOCALE, &status);
88
89 // Strip @.*
90 char *ptr = strchr(actual_locale, '@');
91 if (ptr != NULL) {
92 *ptr = '\0';
93 }
94 if (strcmp(actual_locale, "root") == 0) {
95 actual_locale = "";
96 }
97 if (strcmp(actual_locale, locale) != 0) {
98 printf("\"%s\", \"%s\",\n", actual_locale, get_icu_language_tag(locale));
99 }
100 ucol_close(collator);
101 }
102 return 0;
103 }
104
105compiled and executed using:
106
107 gcc -o main main.c $(pkg-config --libs icu-uc icu-io) && ./main | sort | uniq
108
109--- a/src/backend/commands/collationcmds.c
110+++ b/src/backend/commands/collationcmds.c
111@@ -677,6 +677,715 @@
112
113 return result;
114 }
115+
116+/*
117+ * XXX-Patched: Added a static mapping: collation name (parent) to locale (children)
118+ * I'm gonna burn in hell for this...
119+ */
120+static char* icu_coll_locales[] = {
121+ "", "agq",
122+ "", "agq-CM",
123+ "", "ak",
124+ "", "ak-GH",
125+ "", "asa",
126+ "", "asa-TZ",
127+ "", "ast",
128+ "", "ast-ES",
129+ "", "bas",
130+ "", "bas-CM",
131+ "", "bem",
132+ "", "bem-ZM",
133+ "", "bez",
134+ "", "bez-TZ",
135+ "", "bm",
136+ "", "bm-ML",
137+ "", "brx",
138+ "", "brx-IN",
139+ "", "ca",
140+ "", "ca-AD",
141+ "", "ca-ES",
142+ "", "ca-FR",
143+ "", "ca-IT",
144+ "", "ccp",
145+ "", "ccp-BD",
146+ "", "ccp-IN",
147+ "", "ce",
148+ "", "ce-RU",
149+ "", "cgg",
150+ "", "cgg-UG",
151+ "", "ckb",
152+ "", "ckb-IQ",
153+ "", "ckb-IR",
154+ "", "dav",
155+ "", "dav-KE",
156+ "", "de",
157+ "", "de-AT",
158+ "", "de-BE",
159+ "", "de-CH",
160+ "", "de-DE",
161+ "", "de-IT",
162+ "", "de-LI",
163+ "", "de-LU",
164+ "", "dje",
165+ "", "dje-NE",
166+ "", "doi",
167+ "", "doi-IN",
168+ "", "dua",
169+ "", "dua-CM",
170+ "", "dyo",
171+ "", "dyo-SN",
172+ "", "dz",
173+ "", "dz-BT",
174+ "", "ebu",
175+ "", "ebu-KE",
176+ "", "en",
177+ "", "en-001",
178+ "", "en-150",
179+ "", "en-AE",
180+ "", "en-AG",
181+ "", "en-AI",
182+ "", "en-AS",
183+ "", "en-AT",
184+ "", "en-AU",
185+ "", "en-BB",
186+ "", "en-BE",
187+ "", "en-BI",
188+ "", "en-BM",
189+ "", "en-BS",
190+ "", "en-BW",
191+ "", "en-BZ",
192+ "", "en-CA",
193+ "", "en-CC",
194+ "", "en-CH",
195+ "", "en-CK",
196+ "", "en-CM",
197+ "", "en-CX",
198+ "", "en-CY",
199+ "", "en-DE",
200+ "", "en-DG",
201+ "", "en-DK",
202+ "", "en-DM",
203+ "", "en-ER",
204+ "", "en-FI",
205+ "", "en-FJ",
206+ "", "en-FK",
207+ "", "en-FM",
208+ "", "en-GB",
209+ "", "en-GD",
210+ "", "en-GG",
211+ "", "en-GH",
212+ "", "en-GI",
213+ "", "en-GM",
214+ "", "en-GU",
215+ "", "en-GY",
216+ "", "en-HK",
217+ "", "en-IE",
218+ "", "en-IL",
219+ "", "en-IM",
220+ "", "en-IN",
221+ "", "en-IO",
222+ "", "en-JE",
223+ "", "en-JM",
224+ "", "en-KE",
225+ "", "en-KI",
226+ "", "en-KN",
227+ "", "en-KY",
228+ "", "en-LC",
229+ "", "en-LR",
230+ "", "en-LS",
231+ "", "en-MG",
232+ "", "en-MH",
233+ "", "en-MO",
234+ "", "en-MP",
235+ "", "en-MS",
236+ "", "en-MT",
237+ "", "en-MU",
238+ "", "en-MV",
239+ "", "en-MW",
240+ "", "en-MY",
241+ "", "en-NA",
242+ "", "en-NF",
243+ "", "en-NG",
244+ "", "en-NL",
245+ "", "en-NR",
246+ "", "en-NU",
247+ "", "en-NZ",
248+ "", "en-PG",
249+ "", "en-PH",
250+ "", "en-PK",
251+ "", "en-PN",
252+ "", "en-PR",
253+ "", "en-PW",
254+ "", "en-RW",
255+ "", "en-SB",
256+ "", "en-SC",
257+ "", "en-SD",
258+ "", "en-SE",
259+ "", "en-SG",
260+ "", "en-SH",
261+ "", "en-SI",
262+ "", "en-SL",
263+ "", "en-SS",
264+ "", "en-SX",
265+ "", "en-SZ",
266+ "", "en-TC",
267+ "", "en-TK",
268+ "", "en-TO",
269+ "", "en-TT",
270+ "", "en-TV",
271+ "", "en-TZ",
272+ "", "en-UG",
273+ "", "en-UM",
274+ "", "en-US",
275+ "", "en-VC",
276+ "", "en-VG",
277+ "", "en-VI",
278+ "", "en-VU",
279+ "", "en-WS",
280+ "", "en-ZA",
281+ "", "en-ZM",
282+ "", "en-ZW",
283+ "", "eu",
284+ "", "eu-ES",
285+ "", "ewo",
286+ "", "ewo-CM",
287+ "", "ff",
288+ "", "ff-Latn",
289+ "", "ff-Latn-BF",
290+ "", "ff-Latn-CM",
291+ "", "ff-Latn-GH",
292+ "", "ff-Latn-GM",
293+ "", "ff-Latn-GN",
294+ "", "ff-Latn-GW",
295+ "", "ff-Latn-LR",
296+ "", "ff-Latn-MR",
297+ "", "ff-Latn-NE",
298+ "", "ff-Latn-NG",
299+ "", "ff-Latn-SL",
300+ "", "ff-Latn-SN",
301+ "", "fr",
302+ "", "fr-BE",
303+ "", "fr-BF",
304+ "", "fr-BI",
305+ "", "fr-BJ",
306+ "", "fr-BL",
307+ "", "fr-CD",
308+ "", "fr-CF",
309+ "", "fr-CG",
310+ "", "fr-CH",
311+ "", "fr-CI",
312+ "", "fr-CM",
313+ "", "fr-DJ",
314+ "", "fr-DZ",
315+ "", "fr-FR",
316+ "", "fr-GA",
317+ "", "fr-GF",
318+ "", "fr-GN",
319+ "", "fr-GP",
320+ "", "fr-GQ",
321+ "", "fr-HT",
322+ "", "fr-KM",
323+ "", "fr-LU",
324+ "", "fr-MA",
325+ "", "fr-MC",
326+ "", "fr-MF",
327+ "", "fr-MG",
328+ "", "fr-ML",
329+ "", "fr-MQ",
330+ "", "fr-MR",
331+ "", "fr-MU",
332+ "", "fr-NC",
333+ "", "fr-NE",
334+ "", "fr-PF",
335+ "", "fr-PM",
336+ "", "fr-RE",
337+ "", "fr-RW",
338+ "", "fr-SC",
339+ "", "fr-SN",
340+ "", "fr-SY",
341+ "", "fr-TD",
342+ "", "fr-TG",
343+ "", "fr-TN",
344+ "", "fr-VU",
345+ "", "fr-WF",
346+ "", "fr-YT",
347+ "", "fur",
348+ "", "fur-IT",
349+ "", "fy",
350+ "", "fy-NL",
351+ "", "ga",
352+ "", "ga-GB",
353+ "", "ga-IE",
354+ "", "gd",
355+ "", "gd-GB",
356+ "", "gsw",
357+ "", "gsw-CH",
358+ "", "gsw-FR",
359+ "", "gsw-LI",
360+ "", "guz",
361+ "", "guz-KE",
362+ "", "gv",
363+ "", "gv-IM",
364+ "", "ia",
365+ "", "ia-001",
366+ "", "id",
367+ "", "id-ID",
368+ "", "ii",
369+ "", "ii-CN",
370+ "", "it",
371+ "", "it-CH",
372+ "", "it-IT",
373+ "", "it-SM",
374+ "", "it-VA",
375+ "", "jgo",
376+ "", "jgo-CM",
377+ "", "jmc",
378+ "", "jmc-TZ",
379+ "", "jv",
380+ "", "jv-ID",
381+ "", "kab",
382+ "", "kab-DZ",
383+ "", "kam",
384+ "", "kam-KE",
385+ "", "kde",
386+ "", "kde-TZ",
387+ "", "kea",
388+ "", "kea-CV",
389+ "", "kgp",
390+ "", "kgp-BR",
391+ "", "khq",
392+ "", "khq-ML",
393+ "", "ki",
394+ "", "ki-KE",
395+ "", "kkj",
396+ "", "kkj-CM",
397+ "", "kln",
398+ "", "kln-KE",
399+ "", "ks",
400+ "", "ks-Arab",
401+ "", "ks-Arab-IN",
402+ "", "ks-Deva",
403+ "", "ks-Deva-IN",
404+ "", "ksb",
405+ "", "ksb-TZ",
406+ "", "ksf",
407+ "", "ksf-CM",
408+ "", "ksh",
409+ "", "ksh-DE",
410+ "", "kw",
411+ "", "kw-GB",
412+ "", "lag",
413+ "", "lag-TZ",
414+ "", "lb",
415+ "", "lb-LU",
416+ "", "lg",
417+ "", "lg-UG",
418+ "", "lrc",
419+ "", "lrc-IQ",
420+ "", "lrc-IR",
421+ "", "lu",
422+ "", "lu-CD",
423+ "", "luo",
424+ "", "luo-KE",
425+ "", "luy",
426+ "", "luy-KE",
427+ "", "mai",
428+ "", "mai-IN",
429+ "", "mas",
430+ "", "mas-KE",
431+ "", "mas-TZ",
432+ "", "mer",
433+ "", "mer-KE",
434+ "", "mfe",
435+ "", "mfe-MU",
436+ "", "mg",
437+ "", "mg-MG",
438+ "", "mgh",
439+ "", "mgh-MZ",
440+ "", "mgo",
441+ "", "mgo-CM",
442+ "", "mi",
443+ "", "mi-NZ",
444+ "", "mni",
445+ "", "mni-Beng",
446+ "", "mni-Beng-IN",
447+ "", "ms",
448+ "", "ms-BN",
449+ "", "ms-ID",
450+ "", "ms-MY",
451+ "", "ms-SG",
452+ "", "mua",
453+ "", "mua-CM",
454+ "", "mzn",
455+ "", "mzn-IR",
456+ "", "naq",
457+ "", "naq-NA",
458+ "", "nd",
459+ "", "nd-ZW",
460+ "", "nl",
461+ "", "nl-AW",
462+ "", "nl-BE",
463+ "", "nl-BQ",
464+ "", "nl-CW",
465+ "", "nl-NL",
466+ "", "nl-SR",
467+ "", "nl-SX",
468+ "", "nmg",
469+ "", "nmg-CM",
470+ "", "nnh",
471+ "", "nnh-CM",
472+ "", "nus",
473+ "", "nus-SS",
474+ "", "nyn",
475+ "", "nyn-UG",
476+ "", "os",
477+ "", "os-GE",
478+ "", "os-RU",
479+ "", "pcm",
480+ "", "pcm-NG",
481+ "", "pt",
482+ "", "pt-AO",
483+ "", "pt-BR",
484+ "", "pt-CH",
485+ "", "pt-CV",
486+ "", "pt-GQ",
487+ "", "pt-GW",
488+ "", "pt-LU",
489+ "", "pt-MO",
490+ "", "pt-MZ",
491+ "", "pt-PT",
492+ "", "pt-ST",
493+ "", "pt-TL",
494+ "", "qu",
495+ "", "qu-BO",
496+ "", "qu-EC",
497+ "", "qu-PE",
498+ "", "rm",
499+ "", "rm-CH",
500+ "", "rn",
501+ "", "rn-BI",
502+ "", "rof",
503+ "", "rof-TZ",
504+ "", "rw",
505+ "", "rw-RW",
506+ "", "rwk",
507+ "", "rwk-TZ",
508+ "", "sa",
509+ "", "sa-IN",
510+ "", "sah",
511+ "", "sah-RU",
512+ "", "saq",
513+ "", "saq-KE",
514+ "", "sat",
515+ "", "sat-Olck",
516+ "", "sat-Olck-IN",
517+ "", "sbp",
518+ "", "sbp-TZ",
519+ "", "sc",
520+ "", "sc-IT",
521+ "", "sd",
522+ "", "sd-Arab",
523+ "", "sd-Arab-PK",
524+ "", "sd-Deva",
525+ "", "sd-Deva-IN",
526+ "", "seh",
527+ "", "seh-MZ",
528+ "", "ses",
529+ "", "ses-ML",
530+ "", "sg",
531+ "", "sg-CF",
532+ "", "shi",
533+ "", "shi-Latn",
534+ "", "shi-Latn-MA",
535+ "", "shi-Tfng",
536+ "", "shi-Tfng-MA",
537+ "", "sn",
538+ "", "sn-ZW",
539+ "", "so",
540+ "", "so-DJ",
541+ "", "so-ET",
542+ "", "so-KE",
543+ "", "so-SO",
544+ "", "su",
545+ "", "su-Latn",
546+ "", "su-Latn-ID",
547+ "", "sw",
548+ "", "sw-CD",
549+ "", "sw-KE",
550+ "", "sw-TZ",
551+ "", "sw-UG",
552+ "", "teo",
553+ "", "teo-KE",
554+ "", "teo-UG",
555+ "", "tg",
556+ "", "tg-TJ",
557+ "", "ti",
558+ "", "ti-ER",
559+ "", "ti-ET",
560+ "", "tt",
561+ "", "tt-RU",
562+ "", "twq",
563+ "", "twq-NE",
564+ "", "tzm",
565+ "", "tzm-MA",
566+ "", "vai",
567+ "", "vai-Latn",
568+ "", "vai-Latn-LR",
569+ "", "vai-Vaii",
570+ "", "vai-Vaii-LR",
571+ "", "vun",
572+ "", "vun-TZ",
573+ "", "wae",
574+ "", "wae-CH",
575+ "", "xh",
576+ "", "xh-ZA",
577+ "", "xog",
578+ "", "xog-UG",
579+ "", "yav",
580+ "", "yav-CM",
581+ "", "yrl",
582+ "", "yrl-BR",
583+ "", "yrl-CO",
584+ "", "yrl-VE",
585+ "", "zgh",
586+ "", "zgh-MA",
587+ "", "zu",
588+ "", "zu-ZA",
589+ "af", "af-NA",
590+ "af", "af-ZA",
591+ "am", "am-ET",
592+ "ar", "ar-001",
593+ "ar", "ar-AE",
594+ "ar", "ar-BH",
595+ "ar", "ar-DJ",
596+ "ar", "ar-DZ",
597+ "ar", "ar-EG",
598+ "ar", "ar-EH",
599+ "ar", "ar-ER",
600+ "ar", "ar-IL",
601+ "ar", "ar-IQ",
602+ "ar", "ar-JO",
603+ "ar", "ar-KM",
604+ "ar", "ar-KW",
605+ "ar", "ar-LB",
606+ "ar", "ar-LY",
607+ "ar", "ar-MA",
608+ "ar", "ar-MR",
609+ "ar", "ar-OM",
610+ "ar", "ar-PS",
611+ "ar", "ar-QA",
612+ "ar", "ar-SA",
613+ "ar", "ar-SD",
614+ "ar", "ar-SO",
615+ "ar", "ar-SS",
616+ "ar", "ar-SY",
617+ "ar", "ar-TD",
618+ "ar", "ar-TN",
619+ "ar", "ar-YE",
620+ "as", "as-IN",
621+ "az", "az-Cyrl",
622+ "az", "az-Cyrl-AZ",
623+ "az", "az-Latn",
624+ "az", "az-Latn-AZ",
625+ "be", "be-BY",
626+ "bg", "bg-BG",
627+ "bn", "bn-BD",
628+ "bn", "bn-IN",
629+ "bo", "bo-CN",
630+ "bo", "bo-IN",
631+ "br", "br-FR",
632+ "bs", "bs-Latn",
633+ "bs", "bs-Latn-BA",
634+ "bs_Cyrl", "bs-Cyrl-BA",
635+ "ceb", "ceb-PH",
636+ "chr", "chr-US",
637+ "cs", "cs-CZ",
638+ "cy", "cy-GB",
639+ "da", "da-DK",
640+ "da", "da-GL",
641+ "dsb", "dsb-DE",
642+ "ee", "ee-GH",
643+ "ee", "ee-TG",
644+ "el", "el-CY",
645+ "el", "el-GR",
646+ "eo", "eo-001",
647+ "es", "es-419",
648+ "es", "es-AR",
649+ "es", "es-BO",
650+ "es", "es-BR",
651+ "es", "es-BZ",
652+ "es", "es-CL",
653+ "es", "es-CO",
654+ "es", "es-CR",
655+ "es", "es-CU",
656+ "es", "es-DO",
657+ "es", "es-EA",
658+ "es", "es-EC",
659+ "es", "es-ES",
660+ "es", "es-GQ",
661+ "es", "es-GT",
662+ "es", "es-HN",
663+ "es", "es-IC",
664+ "es", "es-MX",
665+ "es", "es-NI",
666+ "es", "es-PA",
667+ "es", "es-PE",
668+ "es", "es-PH",
669+ "es", "es-PR",
670+ "es", "es-PY",
671+ "es", "es-SV",
672+ "es", "es-US",
673+ "es", "es-UY",
674+ "es", "es-VE",
675+ "et", "et-EE",
676+ "fa", "fa-IR",
677+ "ff_Adlm", "ff-Adlm-BF",
678+ "ff_Adlm", "ff-Adlm-CM",
679+ "ff_Adlm", "ff-Adlm-GH",
680+ "ff_Adlm", "ff-Adlm-GM",
681+ "ff_Adlm", "ff-Adlm-GN",
682+ "ff_Adlm", "ff-Adlm-GW",
683+ "ff_Adlm", "ff-Adlm-LR",
684+ "ff_Adlm", "ff-Adlm-MR",
685+ "ff_Adlm", "ff-Adlm-NE",
686+ "ff_Adlm", "ff-Adlm-NG",
687+ "ff_Adlm", "ff-Adlm-SL",
688+ "ff_Adlm", "ff-Adlm-SN",
689+ "fi", "fi-FI",
690+ "fil", "fil-PH",
691+ "fo", "fo-DK",
692+ "fo", "fo-FO",
693+ "gl", "gl-ES",
694+ "gu", "gu-IN",
695+ "ha", "ha-GH",
696+ "ha", "ha-NE",
697+ "ha", "ha-NG",
698+ "haw", "haw-US",
699+ "he", "he-IL",
700+ "hi", "hi-IN",
701+ "hi", "hi-Latn",
702+ "hi", "hi-Latn-IN",
703+ "hr", "hr-BA",
704+ "hr", "hr-HR",
705+ "hsb", "hsb-DE",
706+ "hu", "hu-HU",
707+ "hy", "hy-AM",
708+ "ig", "ig-NG",
709+ "is", "is-IS",
710+ "ja", "ja-JP",
711+ "ka", "ka-GE",
712+ "kk", "kk-KZ",
713+ "kl", "kl-GL",
714+ "km", "km-KH",
715+ "kn", "kn-IN",
716+ "ko", "ko-KP",
717+ "ko", "ko-KR",
718+ "kok", "kok-IN",
719+ "ku", "ku-TR",
720+ "ky", "ky-KG",
721+ "lkt", "lkt-US",
722+ "ln", "ln-AO",
723+ "ln", "ln-CD",
724+ "ln", "ln-CF",
725+ "ln", "ln-CG",
726+ "lo", "lo-LA",
727+ "lt", "lt-LT",
728+ "lv", "lv-LV",
729+ "mk", "mk-MK",
730+ "ml", "ml-IN",
731+ "mn", "mn-MN",
732+ "mr", "mr-IN",
733+ "mt", "mt-MT",
734+ "my", "my-MM",
735+ "ne", "ne-IN",
736+ "ne", "ne-NP",
737+ "no", "nb",
738+ "no", "nb-NO",
739+ "no", "nb-SJ",
740+ "no", "nn",
741+ "no", "nn-NO",
742+ "om", "om-ET",
743+ "om", "om-KE",
744+ "or", "or-IN",
745+ "pa", "pa-Arab",
746+ "pa", "pa-Arab-PK",
747+ "pa", "pa-Guru",
748+ "pa", "pa-Guru-IN",
749+ "pl", "pl-PL",
750+ "ps", "ps-AF",
751+ "ps", "ps-PK",
752+ "ro", "ro-MD",
753+ "ro", "ro-RO",
754+ "ru", "ru-BY",
755+ "ru", "ru-KG",
756+ "ru", "ru-KZ",
757+ "ru", "ru-MD",
758+ "ru", "ru-RU",
759+ "ru", "ru-UA",
760+ "se", "se-FI",
761+ "se", "se-NO",
762+ "se", "se-SE",
763+ "si", "si-LK",
764+ "sk", "sk-SK",
765+ "sl", "sl-SI",
766+ "smn", "smn-FI",
767+ "sq", "sq-AL",
768+ "sq", "sq-MK",
769+ "sq", "sq-XK",
770+ "sr", "sr-Cyrl",
771+ "sr", "sr-Cyrl-BA",
772+ "sr", "sr-Cyrl-ME",
773+ "sr", "sr-Cyrl-RS",
774+ "sr", "sr-Cyrl-XK",
775+ "sr_Latn", "sr-Latn-BA",
776+ "sr_Latn", "sr-Latn-ME",
777+ "sr_Latn", "sr-Latn-RS",
778+ "sr_Latn", "sr-Latn-XK",
779+ "sv", "sv-AX",
780+ "sv", "sv-FI",
781+ "sv", "sv-SE",
782+ "ta", "ta-IN",
783+ "ta", "ta-LK",
784+ "ta", "ta-MY",
785+ "ta", "ta-SG",
786+ "te", "te-IN",
787+ "th", "th-TH",
788+ "tk", "tk-TM",
789+ "to", "to-TO",
790+ "tr", "tr-CY",
791+ "tr", "tr-TR",
792+ "ug", "ug-CN",
793+ "uk", "uk-UA",
794+ "ur", "ur-IN",
795+ "ur", "ur-PK",
796+ "uz", "uz-Arab",
797+ "uz", "uz-Arab-AF",
798+ "uz", "uz-Cyrl",
799+ "uz", "uz-Cyrl-UZ",
800+ "uz", "uz-Latn",
801+ "uz", "uz-Latn-UZ",
802+ "vi", "vi-VN",
803+ "wo", "wo-SN",
804+ "yi", "yi-001",
805+ "yo", "yo-BJ",
806+ "yo", "yo-NG",
807+ "zh", "yue",
808+ "zh", "yue-Hans",
809+ "zh", "yue-Hans-CN",
810+ "zh", "yue-Hant",
811+ "zh", "yue-Hant-HK",
812+ "zh", "zh-Hans",
813+ "zh", "zh-Hans-CN",
814+ "zh", "zh-Hans-HK",
815+ "zh", "zh-Hans-MO",
816+ "zh", "zh-Hans-SG",
817+ "zh", "zh-Hant",
818+ "zh", "zh-Hant-HK",
819+ "zh", "zh-Hant-MO",
820+ "zh", "zh-Hant-TW",
821+ NULL, NULL,
822+};
823+
824 #endif /* USE_ICU */
825
826
827@@ -987,17 +1696,18 @@
828 * Start the loop at -1 to sneak in the root locale without too much
829 * code duplication.
830 */
831- for (i = -1; i < uloc_countAvailable(); i++)
832+ for (i = -1; i < ucol_countAvailable(); i++) /* XXX-Patched: changed from uloc_countAvailable() */
833 {
834 const char *name;
835 char *langtag;
836 char *icucomment;
837 Oid collid;
838+ char **ptr; /* XXX-Patched: added */
839
840 if (i == -1)
841 name = ""; /* ICU root locale */
842 else
843- name = uloc_getAvailable(i);
844+ name = ucol_getAvailable(i); /* XXX-Patched: changed from uloc_getAvailable() */
845
846 langtag = icu_language_tag(name, ERROR);
847
848@@ -1025,6 +1735,44 @@
849 CreateComments(collid, CollationRelationId, 0,
850 icucomment);
851 }
852+
853+ /*
854+ * XXX-Patched: The following block is added to create collations also for derived
855+ * locales (combination of language+country/region).
856+ * It's terribly inefficient, but in the big picture, it doesn't matter that much
857+ * (it's typically called only once in the life of the cluster).
858+ */
859+ for (ptr = icu_coll_locales; *ptr != NULL; ptr++)
860+ {
861+ /*
862+ * icu_coll_locales is a 1D array of pairs: collation name and locale (langtag).
863+ * ptr++ moves pointer to the second string of the pair and it's a post-increment,
864+ * so after the comparison with name is evaluated.
865+ */
866+ if (strcmp(*ptr++, name) == 0) {
867+ const char *langtag;
868+
869+ langtag = pstrdup(*ptr);
870+ collid = CollationCreate(psprintf("%s-x-icu", langtag),
871+ nspid, GetUserId(),
872+ COLLPROVIDER_ICU, true, -1,
873+ NULL, NULL, langtag, NULL,
874+ get_collation_actual_version(COLLPROVIDER_ICU, langtag),
875+ true, true);
876+
877+ if (OidIsValid(collid))
878+ {
879+ ncreated++;
880+
881+ CommandCounterIncrement();
882+
883+ icucomment = get_icu_locale_comment(langtag);
884+ if (icucomment)
885+ CreateComments(collid, CollationRelationId, 0,
886+ icucomment);
887+ }
888+ }
889+ }
890 }
891 }
892 #endif /* USE_ICU */