Branch data Line data Source code
1 : : /*-----------------------------------------------------------------------
2 : : *
3 : : * PostgreSQL locale utilities for ICU
4 : : *
5 : : * Portions Copyright (c) 2002-2026, PostgreSQL Global Development Group
6 : : *
7 : : * src/backend/utils/adt/pg_locale_icu.c
8 : : *
9 : : *-----------------------------------------------------------------------
10 : : */
11 : :
12 : : #include "postgres.h"
13 : :
14 : : #ifdef USE_ICU
15 : : #include <unicode/ucasemap.h>
16 : : #include <unicode/ucnv.h>
17 : : #include <unicode/ucol.h>
18 : : #include <unicode/ustring.h>
19 : :
20 : : /*
21 : : * We require ICU 55 to be able to use the "und" spelling of the root locale.
22 : : * (Prior versions do not recognize this locale, and moreover fall back to the
23 : : * environment for unrecognized locale names, which could cause confusion and
24 : : * corruption.)
25 : : */
26 : : #if U_ICU_VERSION_MAJOR_NUM < 55
27 : : #error ICU version 55 or later is required
28 : : #endif
29 : : #endif
30 : :
31 : : #include "access/htup_details.h"
32 : : #include "catalog/pg_database.h"
33 : : #include "catalog/pg_collation.h"
34 : : #include "mb/pg_wchar.h"
35 : : #include "miscadmin.h"
36 : : #include "utils/builtins.h"
37 : : #include "utils/formatting.h"
38 : : #include "utils/memutils.h"
39 : : #include "utils/pg_locale.h"
40 : : #include "utils/syscache.h"
41 : :
42 : : /*
43 : : * Size of stack buffer to use for string transformations, used to avoid heap
44 : : * allocations in typical cases. This should be large enough that most strings
45 : : * will fit, but small enough that we feel comfortable putting it on the
46 : : * stack.
47 : : */
48 : : #define TEXTBUFLEN 1024
49 : :
50 : : extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
51 : :
52 : : #ifdef USE_ICU
53 : :
54 : : extern UCollator *pg_ucol_open(const char *loc_str);
55 : : static UCaseMap *pg_ucasemap_open(const char *loc_str);
56 : :
57 : : static size_t strlower_icu(char *dest, size_t destsize, const char *src,
58 : : size_t srclen, pg_locale_t locale);
59 : : static size_t strtitle_icu(char *dest, size_t destsize, const char *src,
60 : : size_t srclen, pg_locale_t locale);
61 : : static size_t strupper_icu(char *dest, size_t destsize, const char *src,
62 : : size_t srclen, pg_locale_t locale);
63 : : static size_t strfold_icu(char *dest, size_t destsize, const char *src,
64 : : size_t srclen, pg_locale_t locale);
65 : : static size_t strlower_icu_utf8(char *dest, size_t destsize, const char *src,
66 : : size_t srclen, pg_locale_t locale);
67 : : static size_t strtitle_icu_utf8(char *dest, size_t destsize, const char *src,
68 : : size_t srclen, pg_locale_t locale);
69 : : static size_t strupper_icu_utf8(char *dest, size_t destsize, const char *src,
70 : : size_t srclen, pg_locale_t locale);
71 : : static size_t strfold_icu_utf8(char *dest, size_t destsize, const char *src,
72 : : size_t srclen, pg_locale_t locale);
73 : : static size_t downcase_ident_icu(char *dst, size_t dstsize, const char *src,
74 : : size_t srclen, pg_locale_t locale);
75 : : static int strncoll_icu(const char *arg1, size_t len1,
76 : : const char *arg2, size_t len2,
77 : : pg_locale_t locale);
78 : : static int strcoll_icu(const char *arg1, const char *arg2,
79 : : pg_locale_t locale);
80 : : static size_t strnxfrm_icu(char *dest, size_t destsize,
81 : : const char *src, size_t srclen,
82 : : pg_locale_t locale);
83 : : static size_t strxfrm_icu(char *dest, size_t destsize, const char *src,
84 : : pg_locale_t locale);
85 : : extern char *get_collation_actual_version_icu(const char *collcollate);
86 : :
87 : : typedef int32_t (*ICU_Convert_Func) (UChar *dest, int32_t destCapacity,
88 : : const UChar *src, int32_t srcLength,
89 : : const char *locale,
90 : : UErrorCode *pErrorCode);
91 : :
92 : : /*
93 : : * Converter object for converting between ICU's UChar strings and C strings
94 : : * in database encoding. Since the database encoding doesn't change, we only
95 : : * need one of these per session.
96 : : */
97 : : static UConverter *icu_converter = NULL;
98 : :
99 : : static UCollator *make_icu_collator(const char *iculocstr,
100 : : const char *icurules);
101 : : static size_t strnxfrm_prefix_icu(char *dest, size_t destsize,
102 : : const char *src, size_t srclen,
103 : : pg_locale_t locale);
104 : : static size_t strxfrm_prefix_icu(char *dest, size_t destsize, const char *src,
105 : : pg_locale_t locale);
106 : : static int strncoll_icu_utf8(const char *arg1, size_t len1,
107 : : const char *arg2, size_t len2,
108 : : pg_locale_t locale);
109 : : static int strcoll_icu_utf8(const char *arg1,
110 : : const char *arg2,
111 : : pg_locale_t locale);
112 : : static size_t strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
113 : : const char *src, size_t srclen,
114 : : pg_locale_t locale);
115 : : static size_t strxfrm_prefix_icu_utf8(char *dest, size_t destsize, const char *src,
116 : : pg_locale_t locale);
117 : : static void init_icu_converter(void);
118 : : static int32_t uchar_length(UConverter *converter,
119 : : const char *str, int32_t len);
120 : : static int32_t uchar_convert(UConverter *converter,
121 : : UChar *dest, int32_t destlen,
122 : : const char *src, int32_t srclen);
123 : : static int32_t icu_to_uchar(UChar **buff_uchar, const char *buff,
124 : : size_t nbytes);
125 : : static size_t icu_from_uchar(char *dest, size_t destsize,
126 : : const UChar *buff_uchar, int32_t len_uchar);
127 : : static void icu_set_collation_attributes(UCollator *collator, const char *loc,
128 : : UErrorCode *status);
129 : : static int32_t icu_convert_case(ICU_Convert_Func func, char *dest,
130 : : size_t destsize, const char *src,
131 : : size_t srclen, pg_locale_t locale);
132 : : static int32_t u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
133 : : const UChar *src, int32_t srcLength,
134 : : const char *locale,
135 : : UErrorCode *pErrorCode);
136 : : static int32_t u_strFoldCase_default(UChar *dest, int32_t destCapacity,
137 : : const UChar *src, int32_t srcLength,
138 : : const char *locale,
139 : : UErrorCode *pErrorCode);
140 : : static int32_t foldcase_options(const char *locale);
141 : :
142 : : /*
143 : : * XXX: many of the functions below rely on casts directly from pg_wchar to
144 : : * UChar32, which is correct for UTF-8 and LATIN1, but not in general.
145 : : */
146 : :
147 : : static pg_wchar
148 : 72 : toupper_icu(pg_wchar wc, pg_locale_t locale)
149 : : {
150 : 72 : return u_toupper(wc);
151 : : }
152 : :
153 : : static pg_wchar
154 : 72 : tolower_icu(pg_wchar wc, pg_locale_t locale)
155 : : {
156 : 72 : return u_tolower(wc);
157 : : }
158 : :
159 : : static const struct collate_methods collate_methods_icu = {
160 : : .strncoll = strncoll_icu,
161 : : .strcoll = strcoll_icu,
162 : : .strnxfrm = strnxfrm_icu,
163 : : .strxfrm = strxfrm_icu,
164 : : .strnxfrm_prefix = strnxfrm_prefix_icu,
165 : : .strxfrm_prefix = strxfrm_prefix_icu,
166 : : .strxfrm_is_safe = true,
167 : : };
168 : :
169 : : static const struct collate_methods collate_methods_icu_utf8 = {
170 : : .strncoll = strncoll_icu_utf8,
171 : : .strcoll = strcoll_icu_utf8,
172 : : .strnxfrm = strnxfrm_icu,
173 : : .strxfrm = strxfrm_icu,
174 : : .strnxfrm_prefix = strnxfrm_prefix_icu_utf8,
175 : : .strxfrm_prefix = strxfrm_prefix_icu_utf8,
176 : : .strxfrm_is_safe = true,
177 : : };
178 : :
179 : : static bool
180 : 8192 : wc_isdigit_icu(pg_wchar wc, pg_locale_t locale)
181 : : {
182 : 8192 : return u_isdigit(wc);
183 : : }
184 : :
185 : : static bool
186 : 8192 : wc_isalpha_icu(pg_wchar wc, pg_locale_t locale)
187 : : {
188 : 8192 : return u_isalpha(wc);
189 : : }
190 : :
191 : : static bool
192 : 8192 : wc_isalnum_icu(pg_wchar wc, pg_locale_t locale)
193 : : {
194 : 8192 : return u_isalnum(wc);
195 : : }
196 : :
197 : : static bool
198 : 8192 : wc_isupper_icu(pg_wchar wc, pg_locale_t locale)
199 : : {
200 : 8192 : return u_isupper(wc);
201 : : }
202 : :
203 : : static bool
204 : 8192 : wc_islower_icu(pg_wchar wc, pg_locale_t locale)
205 : : {
206 : 8192 : return u_islower(wc);
207 : : }
208 : :
209 : : static bool
210 : 8192 : wc_isgraph_icu(pg_wchar wc, pg_locale_t locale)
211 : : {
212 : 8192 : return u_isgraph(wc);
213 : : }
214 : :
215 : : static bool
216 : 8192 : wc_isprint_icu(pg_wchar wc, pg_locale_t locale)
217 : : {
218 : 8192 : return u_isprint(wc);
219 : : }
220 : :
221 : : static bool
222 : 8192 : wc_ispunct_icu(pg_wchar wc, pg_locale_t locale)
223 : : {
224 : 8192 : return u_ispunct(wc);
225 : : }
226 : :
227 : : static bool
228 : 8192 : wc_isspace_icu(pg_wchar wc, pg_locale_t locale)
229 : : {
230 : 8192 : return u_isspace(wc);
231 : : }
232 : :
233 : : static bool
234 : 0 : wc_isxdigit_icu(pg_wchar wc, pg_locale_t locale)
235 : : {
236 : 0 : return u_isxdigit(wc);
237 : : }
238 : :
239 : : static bool
240 : 105 : wc_iscased_icu(pg_wchar wc, pg_locale_t locale)
241 : : {
242 : 105 : return u_hasBinaryProperty(wc, UCHAR_CASED);
243 : : }
244 : :
245 : : static const struct ctype_methods ctype_methods_icu = {
246 : : .strlower = strlower_icu,
247 : : .strtitle = strtitle_icu,
248 : : .strupper = strupper_icu,
249 : : .strfold = strfold_icu,
250 : : .downcase_ident = downcase_ident_icu,
251 : : .wc_isdigit = wc_isdigit_icu,
252 : : .wc_isalpha = wc_isalpha_icu,
253 : : .wc_isalnum = wc_isalnum_icu,
254 : : .wc_isupper = wc_isupper_icu,
255 : : .wc_islower = wc_islower_icu,
256 : : .wc_isgraph = wc_isgraph_icu,
257 : : .wc_isprint = wc_isprint_icu,
258 : : .wc_ispunct = wc_ispunct_icu,
259 : : .wc_isspace = wc_isspace_icu,
260 : : .wc_isxdigit = wc_isxdigit_icu,
261 : : .wc_iscased = wc_iscased_icu,
262 : : .wc_toupper = toupper_icu,
263 : : .wc_tolower = tolower_icu,
264 : : };
265 : :
266 : : static const struct ctype_methods ctype_methods_icu_utf8 = {
267 : : .strlower = strlower_icu_utf8,
268 : : .strtitle = strtitle_icu_utf8,
269 : : .strupper = strupper_icu_utf8,
270 : : .strfold = strfold_icu_utf8,
271 : : /* uses plain ASCII semantics for historical reasons */
272 : : .downcase_ident = NULL,
273 : : .wc_isdigit = wc_isdigit_icu,
274 : : .wc_isalpha = wc_isalpha_icu,
275 : : .wc_isalnum = wc_isalnum_icu,
276 : : .wc_isupper = wc_isupper_icu,
277 : : .wc_islower = wc_islower_icu,
278 : : .wc_isgraph = wc_isgraph_icu,
279 : : .wc_isprint = wc_isprint_icu,
280 : : .wc_ispunct = wc_ispunct_icu,
281 : : .wc_isspace = wc_isspace_icu,
282 : : .wc_isxdigit = wc_isxdigit_icu,
283 : : .wc_iscased = wc_iscased_icu,
284 : : .wc_toupper = toupper_icu,
285 : : .wc_tolower = tolower_icu,
286 : : };
287 : :
288 : : /*
289 : : * ICU still depends on libc for compatibility with certain historical
290 : : * behavior for single-byte encodings. See downcase_ident_icu().
291 : : *
292 : : * XXX: consider fixing by decoding the single byte into a code point, and
293 : : * using u_tolower().
294 : : */
295 : : static locale_t
296 : 0 : make_libc_ctype_locale(const char *ctype)
297 : : {
298 : : locale_t loc;
299 : :
300 : : #ifndef WIN32
301 : 0 : loc = newlocale(LC_CTYPE_MASK, ctype, NULL);
302 : : #else
303 : : loc = _create_locale(LC_ALL, ctype);
304 : : #endif
305 [ # # ]: 0 : if (!loc)
306 : 0 : report_newlocale_failure(ctype);
307 : :
308 : 0 : return loc;
309 : : }
310 : : #endif /* USE_ICU */
311 : :
312 : : pg_locale_t
313 : 137 : create_pg_locale_icu(Oid collid, MemoryContext context)
314 : : {
315 : : #ifdef USE_ICU
316 : : bool deterministic;
317 : : const char *iculocstr;
318 : 137 : const char *icurules = NULL;
319 : : UCollator *collator;
320 : 137 : locale_t loc = (locale_t) 0;
321 : : pg_locale_t result;
322 : :
323 [ + + ]: 137 : if (collid == DEFAULT_COLLATION_OID)
324 : : {
325 : : HeapTuple tp;
326 : : Datum datum;
327 : : bool isnull;
328 : :
329 : 13 : tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
330 [ - + ]: 13 : if (!HeapTupleIsValid(tp))
331 [ # # ]: 0 : elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
332 : :
333 : : /* default database collation is always deterministic */
334 : 13 : deterministic = true;
335 : 13 : datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
336 : : Anum_pg_database_datlocale);
337 : 13 : iculocstr = TextDatumGetCString(datum);
338 : 13 : datum = SysCacheGetAttr(DATABASEOID, tp,
339 : : Anum_pg_database_daticurules, &isnull);
340 [ - + ]: 13 : if (!isnull)
341 : 0 : icurules = TextDatumGetCString(datum);
342 : :
343 : : /* libc only needed for default locale and single-byte encoding */
344 [ - + ]: 13 : if (pg_database_encoding_max_length() == 1)
345 : : {
346 : : const char *ctype;
347 : :
348 : 0 : datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
349 : : Anum_pg_database_datctype);
350 : 0 : ctype = TextDatumGetCString(datum);
351 : :
352 : 0 : loc = make_libc_ctype_locale(ctype);
353 : : }
354 : :
355 : 13 : ReleaseSysCache(tp);
356 : : }
357 : : else
358 : : {
359 : : Form_pg_collation collform;
360 : : HeapTuple tp;
361 : : Datum datum;
362 : : bool isnull;
363 : :
364 : 124 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
365 [ - + ]: 124 : if (!HeapTupleIsValid(tp))
366 [ # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
367 : 124 : collform = (Form_pg_collation) GETSTRUCT(tp);
368 : 124 : deterministic = collform->collisdeterministic;
369 : 124 : datum = SysCacheGetAttrNotNull(COLLOID, tp,
370 : : Anum_pg_collation_colllocale);
371 : 124 : iculocstr = TextDatumGetCString(datum);
372 : 124 : datum = SysCacheGetAttr(COLLOID, tp,
373 : : Anum_pg_collation_collicurules, &isnull);
374 [ + + ]: 124 : if (!isnull)
375 : 12 : icurules = TextDatumGetCString(datum);
376 : :
377 : 124 : ReleaseSysCache(tp);
378 : : }
379 : :
380 : 137 : collator = make_icu_collator(iculocstr, icurules);
381 : :
382 : 131 : result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
383 : 131 : result->icu.locale = MemoryContextStrdup(context, iculocstr);
384 : 131 : result->icu.ucol = collator;
385 : 131 : result->icu.lt = loc;
386 : 131 : result->deterministic = deterministic;
387 : 131 : result->collate_is_c = false;
388 : 131 : result->ctype_is_c = false;
389 [ + - ]: 131 : if (GetDatabaseEncoding() == PG_UTF8)
390 : : {
391 : 131 : result->icu.ucasemap = pg_ucasemap_open(iculocstr);
392 : 131 : result->collate = &collate_methods_icu_utf8;
393 : 131 : result->ctype = &ctype_methods_icu_utf8;
394 : : }
395 : : else
396 : : {
397 : 0 : result->collate = &collate_methods_icu;
398 : 0 : result->ctype = &ctype_methods_icu;
399 : : }
400 : :
401 : 131 : return result;
402 : : #else /* not USE_ICU */
403 : : /* could get here if a collation was created by a build with ICU */
404 : : ereport(ERROR,
405 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
406 : : errmsg("ICU is not supported in this build")));
407 : :
408 : : return NULL;
409 : : #endif /* not USE_ICU */
410 : : }
411 : :
412 : : #ifdef USE_ICU
413 : :
414 : : /*
415 : : * Check locale string and fix it if necessary. Returns a new palloc'd string.
416 : : */
417 : : static char *
418 : 48512 : fix_icu_locale_str(const char *loc_str)
419 : : {
420 : : /*
421 : : * Must never open default collator, because it depends on the environment
422 : : * and may change at any time. Should not happen, but check here to catch
423 : : * bugs that might be hard to catch otherwise.
424 : : *
425 : : * NB: the default collator is not the same as the collator for the root
426 : : * locale. The root locale may be specified as the empty string, "und", or
427 : : * "root". The default collator is opened by passing NULL to ucol_open().
428 : : */
429 [ - + ]: 48512 : if (loc_str == NULL)
430 [ # # ]: 0 : elog(ERROR, "opening default collator is not supported");
431 : :
432 : : /*
433 : : * XXX There are currently no fixups required, but they could be added
434 : : * here.
435 : : */
436 : :
437 : 48512 : return pstrdup(loc_str);
438 : : }
439 : :
440 : : /*
441 : : * Wrapper around ucol_open() to handle API differences for older ICU
442 : : * versions.
443 : : *
444 : : * Ensure that no path leaks a UCollator.
445 : : */
446 : : UCollator *
447 : 48381 : pg_ucol_open(const char *loc_str)
448 : : {
449 : : UCollator *collator;
450 : : UErrorCode status;
451 : : char *fixed_str;
452 : :
453 : 48381 : fixed_str = fix_icu_locale_str(loc_str);
454 : :
455 : 48381 : status = U_ZERO_ERROR;
456 : 48381 : collator = ucol_open(fixed_str, &status);
457 [ + + ]: 48381 : if (U_FAILURE(status))
458 [ + - ]: 7 : ereport(ERROR,
459 : : /* use original string for error report */
460 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
461 : : errmsg("could not open collator for locale \"%s\": %s",
462 : : loc_str, u_errorName(status))));
463 : :
464 : 48374 : pfree(fixed_str);
465 : :
466 : 48374 : return collator;
467 : : }
468 : :
469 : : /*
470 : : * Wrapper around ucasemap_open() to handle API differences for older ICU
471 : : * versions.
472 : : *
473 : : * Additionally makes sure we get the right options for case folding.
474 : : */
475 : : static UCaseMap *
476 : 131 : pg_ucasemap_open(const char *loc_str)
477 : : {
478 : 131 : UErrorCode status = U_ZERO_ERROR;
479 : : UCaseMap *casemap;
480 : : char *fixed_str;
481 : :
482 : 131 : fixed_str = fix_icu_locale_str(loc_str);
483 : :
484 : 131 : casemap = ucasemap_open(fixed_str, foldcase_options(fixed_str), &status);
485 [ - + ]: 131 : if (U_FAILURE(status))
486 : : /* use original string for error report */
487 [ # # ]: 0 : ereport(ERROR,
488 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
489 : : errmsg("could not open casemap for locale \"%s\": %s",
490 : : loc_str, u_errorName(status)));
491 : :
492 : 131 : pfree(fixed_str);
493 : :
494 : 131 : return casemap;
495 : : }
496 : :
497 : : /*
498 : : * Create a UCollator with the given locale string and rules.
499 : : *
500 : : * Ensure that no path leaks a UCollator.
501 : : */
502 : : static UCollator *
503 : 137 : make_icu_collator(const char *iculocstr, const char *icurules)
504 : : {
505 [ + + ]: 137 : if (!icurules)
506 : : {
507 : : /* simple case without rules */
508 : 125 : return pg_ucol_open(iculocstr);
509 : : }
510 : : else
511 : : {
512 : : UCollator *collator_std_rules;
513 : : UCollator *collator_all_rules;
514 : : const UChar *std_rules;
515 : : UChar *my_rules;
516 : : UChar *all_rules;
517 : : int32_t length;
518 : : int32_t total;
519 : : UErrorCode status;
520 : :
521 : : /*
522 : : * If rules are specified, we extract the rules of the standard
523 : : * collation, add our own rules, and make a new collator with the
524 : : * combined rules.
525 : : */
526 : 12 : icu_to_uchar(&my_rules, icurules, strlen(icurules));
527 : :
528 : 12 : collator_std_rules = pg_ucol_open(iculocstr);
529 : :
530 : 12 : std_rules = ucol_getRules(collator_std_rules, &length);
531 : :
532 : 12 : total = u_strlen(std_rules) + u_strlen(my_rules) + 1;
533 : :
534 : : /* avoid leaking collator on OOM */
535 : 12 : all_rules = palloc_array_extended(UChar, total, MCXT_ALLOC_NO_OOM);
536 [ - + ]: 12 : if (!all_rules)
537 : : {
538 : 0 : ucol_close(collator_std_rules);
539 [ # # ]: 0 : ereport(ERROR,
540 : : (errcode(ERRCODE_OUT_OF_MEMORY),
541 : : errmsg("out of memory")));
542 : : }
543 : :
544 : 12 : u_strcpy(all_rules, std_rules);
545 : 12 : u_strcat(all_rules, my_rules);
546 : :
547 : 12 : ucol_close(collator_std_rules);
548 : :
549 : 12 : status = U_ZERO_ERROR;
550 : 12 : collator_all_rules = ucol_openRules(all_rules, u_strlen(all_rules),
551 : : UCOL_DEFAULT, UCOL_DEFAULT,
552 : : NULL, &status);
553 [ + + ]: 12 : if (U_FAILURE(status))
554 : : {
555 [ + - ]: 4 : ereport(ERROR,
556 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557 : : errmsg("could not open collator for locale \"%s\" with rules \"%s\": %s",
558 : : iculocstr, icurules, u_errorName(status))));
559 : : }
560 : :
561 : 8 : pfree(my_rules);
562 : 8 : pfree(all_rules);
563 : 8 : return collator_all_rules;
564 : : }
565 : : }
566 : :
567 : : static size_t
568 : 0 : strlower_icu(char *dest, size_t destsize, const char *src, size_t srclen,
569 : : pg_locale_t locale)
570 : : {
571 : 0 : return icu_convert_case(u_strToLower, dest, destsize, src, srclen, locale);
572 : : }
573 : :
574 : : static size_t
575 : 0 : strtitle_icu(char *dest, size_t destsize, const char *src, size_t srclen,
576 : : pg_locale_t locale)
577 : : {
578 : 0 : return icu_convert_case(u_strToTitle_default_BI, dest, destsize, src, srclen, locale);
579 : : }
580 : :
581 : : static size_t
582 : 0 : strupper_icu(char *dest, size_t destsize, const char *src, size_t srclen,
583 : : pg_locale_t locale)
584 : : {
585 : 0 : return icu_convert_case(u_strToUpper, dest, destsize, src, srclen, locale);
586 : : }
587 : :
588 : : static size_t
589 : 0 : strfold_icu(char *dest, size_t destsize, const char *src, size_t srclen,
590 : : pg_locale_t locale)
591 : : {
592 : 0 : return icu_convert_case(u_strFoldCase_default, dest, destsize, src, srclen, locale);
593 : : }
594 : :
595 : : static size_t
596 : 364 : strlower_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
597 : : pg_locale_t locale)
598 : : {
599 : 364 : UErrorCode status = U_ZERO_ERROR;
600 : : int32_t needed;
601 : :
602 : 364 : needed = ucasemap_utf8ToLower(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
603 [ - + - - ]: 364 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
604 [ # # ]: 0 : ereport(ERROR,
605 : : errmsg("case conversion failed: %s", u_errorName(status)));
606 : 364 : return needed;
607 : : }
608 : :
609 : : static size_t
610 : 20 : strtitle_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
611 : : pg_locale_t locale)
612 : : {
613 : 20 : UErrorCode status = U_ZERO_ERROR;
614 : : int32_t needed;
615 : :
616 : 20 : needed = ucasemap_utf8ToTitle(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
617 [ - + - - ]: 20 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
618 [ # # ]: 0 : ereport(ERROR,
619 : : errmsg("case conversion failed: %s", u_errorName(status)));
620 : 20 : return needed;
621 : : }
622 : :
623 : : static size_t
624 : 72 : strupper_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
625 : : pg_locale_t locale)
626 : : {
627 : 72 : UErrorCode status = U_ZERO_ERROR;
628 : : int32_t needed;
629 : :
630 : 72 : needed = ucasemap_utf8ToUpper(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
631 [ - + - - ]: 72 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
632 [ # # ]: 0 : ereport(ERROR,
633 : : errmsg("case conversion failed: %s", u_errorName(status)));
634 : 72 : return needed;
635 : : }
636 : :
637 : : static size_t
638 : 10 : strfold_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
639 : : pg_locale_t locale)
640 : : {
641 : 10 : UErrorCode status = U_ZERO_ERROR;
642 : : int32_t needed;
643 : :
644 : 10 : needed = ucasemap_utf8FoldCase(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
645 [ - + - - ]: 10 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
646 [ # # ]: 0 : ereport(ERROR,
647 : : errmsg("case conversion failed: %s", u_errorName(status)));
648 : 10 : return needed;
649 : : }
650 : :
651 : : /*
652 : : * For historical compatibility, behavior is not multibyte-aware.
653 : : *
654 : : * NB: uses libc tolower() for single-byte encodings (also for historical
655 : : * compatibility), and therefore relies on the global LC_CTYPE setting.
656 : : */
657 : : static size_t
658 : 0 : downcase_ident_icu(char *dst, size_t dstsize, const char *src,
659 : : size_t srclen, pg_locale_t locale)
660 : : {
661 : : size_t i;
662 : : bool libc_lower;
663 : 0 : locale_t lt = locale->icu.lt;
664 : :
665 [ # # # # ]: 0 : libc_lower = lt && (pg_database_encoding_max_length() == 1);
666 : :
667 [ # # # # ]: 0 : for (i = 0; i < srclen && i < dstsize; i++)
668 : : {
669 : 0 : unsigned char ch = (unsigned char) src[i];
670 : :
671 [ # # # # ]: 0 : if (ch >= 'A' && ch <= 'Z')
672 : 0 : ch = pg_ascii_tolower(ch);
673 [ # # # # : 0 : else if (libc_lower && IS_HIGHBIT_SET(ch) && isupper_l(ch, lt))
# # ]
674 : 0 : ch = tolower_l(ch, lt);
675 : 0 : dst[i] = (char) ch;
676 : : }
677 : :
678 [ # # ]: 0 : if (i < dstsize)
679 : 0 : dst[i] = '\0';
680 : :
681 : 0 : return srclen;
682 : : }
683 : :
684 : : /*
685 : : * strncoll_icu_utf8
686 : : *
687 : : * Call ucol_strcollUTF8() or ucol_strcoll() as appropriate for the given
688 : : * database encoding.
689 : : */
690 : : int
691 : 17681 : strncoll_icu_utf8(const char *arg1, size_t len1, const char *arg2, size_t len2,
692 : : pg_locale_t locale)
693 : : {
694 : : int result;
695 : : UErrorCode status;
696 : :
697 : : Assert(GetDatabaseEncoding() == PG_UTF8);
698 : :
699 : 17681 : status = U_ZERO_ERROR;
700 : 17681 : result = ucol_strcollUTF8(locale->icu.ucol,
701 : : arg1, len1,
702 : : arg2, len2,
703 : : &status);
704 [ - + ]: 17681 : if (U_FAILURE(status))
705 [ # # ]: 0 : ereport(ERROR,
706 : : (errmsg("collation failed: %s", u_errorName(status))));
707 : :
708 : 17681 : return result;
709 : : }
710 : :
711 : : int
712 : 1140 : strcoll_icu_utf8(const char *arg1, const char *arg2, pg_locale_t locale)
713 : : {
714 : : int result;
715 : : UErrorCode status;
716 : :
717 : : Assert(GetDatabaseEncoding() == PG_UTF8);
718 : :
719 : 1140 : status = U_ZERO_ERROR;
720 : 1140 : result = ucol_strcollUTF8(locale->icu.ucol,
721 : : arg1, -1,
722 : : arg2, -1,
723 : : &status);
724 [ - + ]: 1140 : if (U_FAILURE(status))
725 [ # # ]: 0 : ereport(ERROR,
726 : : (errmsg("collation failed: %s", u_errorName(status))));
727 : :
728 : 1140 : return result;
729 : : }
730 : :
731 : : static size_t
732 : 8328 : strnxfrm_icu_internal(char *dest, size_t destsize, const char *src, ssize_t srclen,
733 : : pg_locale_t locale)
734 : : {
735 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
736 : 8328 : UChar *uchar = sbuf;
737 : : int32_t ulen;
738 : : Size result_bsize;
739 : :
740 : 8328 : init_icu_converter();
741 : :
742 : 8328 : ulen = uchar_length(icu_converter, src, srclen);
743 : :
744 [ - + ]: 8328 : if (ulen >= lengthof(sbuf))
745 : 0 : uchar = palloc_array(UChar, ulen + 1);
746 : :
747 : 8328 : ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
748 : :
749 : 8328 : result_bsize = ucol_getSortKey(locale->icu.ucol,
750 : : uchar, ulen,
751 : : (uint8_t *) dest, destsize);
752 : :
753 : : /*
754 : : * ucol_getSortKey() counts the nul-terminator in the result length, but
755 : : * this function should not.
756 : : */
757 : : Assert(result_bsize > 0);
758 : 8328 : result_bsize--;
759 : :
760 [ - + ]: 8328 : if (uchar != sbuf)
761 : 0 : pfree(uchar);
762 : :
763 : : /* if dest is defined, it should be nul-terminated */
764 : : Assert(result_bsize >= destsize || dest[result_bsize] == '\0');
765 : :
766 : 8328 : return result_bsize;
767 : : }
768 : :
769 : : static size_t
770 : 8328 : strnxfrm_icu(char *dest, size_t destsize, const char *src, size_t srclen,
771 : : pg_locale_t locale)
772 : : {
773 : 8328 : return strnxfrm_icu_internal(dest, destsize, src, srclen, locale);
774 : : }
775 : :
776 : : static size_t
777 : 0 : strxfrm_icu(char *dest, size_t destsize, const char *src,
778 : : pg_locale_t locale)
779 : : {
780 : 0 : return strnxfrm_icu_internal(dest, destsize, src, -1, locale);
781 : : }
782 : :
783 : : static size_t
784 : 1302 : strnxfrm_prefix_icu_utf8_internal(char *dest, size_t destsize,
785 : : const char *src, ssize_t srclen,
786 : : pg_locale_t locale)
787 : : {
788 : : size_t result;
789 : : UCharIterator iter;
790 : : uint32_t state[2];
791 : : UErrorCode status;
792 : :
793 : : Assert(GetDatabaseEncoding() == PG_UTF8);
794 : :
795 : 1302 : uiter_setUTF8(&iter, src, srclen);
796 : 1302 : state[0] = state[1] = 0; /* won't need that again */
797 : 1302 : status = U_ZERO_ERROR;
798 : 1302 : result = ucol_nextSortKeyPart(locale->icu.ucol,
799 : : &iter,
800 : : state,
801 : : (uint8_t *) dest,
802 : : destsize,
803 : : &status);
804 [ - + ]: 1302 : if (U_FAILURE(status))
805 [ # # ]: 0 : ereport(ERROR,
806 : : (errmsg("sort key generation failed: %s",
807 : : u_errorName(status))));
808 : :
809 : 1302 : return result;
810 : : }
811 : :
812 : : static size_t
813 : 0 : strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
814 : : const char *src, size_t srclen,
815 : : pg_locale_t locale)
816 : : {
817 : 0 : return strnxfrm_prefix_icu_utf8_internal(dest, destsize, src, srclen, locale);
818 : : }
819 : :
820 : : static size_t
821 : 1302 : strxfrm_prefix_icu_utf8(char *dest, size_t destsize, const char *src,
822 : : pg_locale_t locale)
823 : : {
824 : 1302 : return strnxfrm_prefix_icu_utf8_internal(dest, destsize, src, -1, locale);
825 : : }
826 : :
827 : : char *
828 : 48147 : get_collation_actual_version_icu(const char *collcollate)
829 : : {
830 : : UCollator *collator;
831 : : UVersionInfo versioninfo;
832 : : char buf[U_MAX_VERSION_STRING_LENGTH];
833 : :
834 : 48147 : collator = pg_ucol_open(collcollate);
835 : :
836 : 48147 : ucol_getVersion(collator, versioninfo);
837 : 48147 : ucol_close(collator);
838 : :
839 : 48147 : u_versionToString(versioninfo, buf);
840 : 48147 : return pstrdup(buf);
841 : : }
842 : :
843 : : /*
844 : : * Convert a string in the database encoding into a string of UChars.
845 : : *
846 : : * The source string at buff is of length nbytes
847 : : * (it needn't be nul-terminated)
848 : : *
849 : : * *buff_uchar receives a pointer to the palloc'd result string, and
850 : : * the function's result is the number of UChars generated.
851 : : *
852 : : * The result string is nul-terminated, though most callers rely on the
853 : : * result length instead.
854 : : */
855 : : static int32_t
856 : 12 : icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
857 : : {
858 : : int32_t len_uchar;
859 : :
860 : 12 : init_icu_converter();
861 : :
862 : 12 : len_uchar = uchar_length(icu_converter, buff, nbytes);
863 : :
864 : 12 : *buff_uchar = palloc_array(UChar, len_uchar + 1);
865 : 12 : len_uchar = uchar_convert(icu_converter,
866 : : *buff_uchar, len_uchar + 1, buff, nbytes);
867 : :
868 : 12 : return len_uchar;
869 : : }
870 : :
871 : : /*
872 : : * Convert a string of UChars into the database encoding.
873 : : *
874 : : * The source string at buff_uchar is of length len_uchar
875 : : * (it needn't be nul-terminated)
876 : : *
877 : : * *result receives a pointer to the palloc'd result string, and the
878 : : * function's result is the number of bytes generated (not counting nul).
879 : : *
880 : : * The result string is nul-terminated.
881 : : */
882 : : static size_t
883 : 0 : icu_from_uchar(char *dest, size_t destsize, const UChar *buff_uchar, int32_t len_uchar)
884 : : {
885 : : UErrorCode status;
886 : : int32_t len_result;
887 : :
888 : 0 : init_icu_converter();
889 : :
890 : 0 : status = U_ZERO_ERROR;
891 : 0 : len_result = ucnv_fromUChars(icu_converter, NULL, 0,
892 : : buff_uchar, len_uchar, &status);
893 [ # # # # ]: 0 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
894 [ # # ]: 0 : ereport(ERROR,
895 : : (errmsg("%s failed: %s", "ucnv_fromUChars",
896 : : u_errorName(status))));
897 : :
898 [ # # ]: 0 : if (len_result + 1 > destsize)
899 : 0 : return len_result;
900 : :
901 : 0 : status = U_ZERO_ERROR;
902 : 0 : len_result = ucnv_fromUChars(icu_converter, dest, len_result + 1,
903 : : buff_uchar, len_uchar, &status);
904 [ # # ]: 0 : if (U_FAILURE(status) ||
905 [ # # ]: 0 : status == U_STRING_NOT_TERMINATED_WARNING)
906 [ # # ]: 0 : ereport(ERROR,
907 : : (errmsg("%s failed: %s", "ucnv_fromUChars",
908 : : u_errorName(status))));
909 : :
910 : 0 : return len_result;
911 : : }
912 : :
913 : : static int32_t
914 : 0 : convert_case_uchar(ICU_Convert_Func func, pg_locale_t mylocale,
915 : : UChar **buff_dest, UChar *buff_source, int32_t len_source)
916 : : {
917 : : UErrorCode status;
918 : : int32_t len_dest;
919 : :
920 : 0 : len_dest = len_source; /* try first with same length */
921 : 0 : *buff_dest = palloc_array(UChar, len_dest);
922 : 0 : status = U_ZERO_ERROR;
923 : 0 : len_dest = func(*buff_dest, len_dest, buff_source, len_source,
924 : : mylocale->icu.locale, &status);
925 [ # # ]: 0 : if (status == U_BUFFER_OVERFLOW_ERROR)
926 : : {
927 : : /* try again with adjusted length */
928 : 0 : pfree(*buff_dest);
929 : 0 : *buff_dest = palloc_array(UChar, len_dest);
930 : 0 : status = U_ZERO_ERROR;
931 : 0 : len_dest = func(*buff_dest, len_dest, buff_source, len_source,
932 : : mylocale->icu.locale, &status);
933 : : }
934 [ # # ]: 0 : if (U_FAILURE(status))
935 [ # # ]: 0 : ereport(ERROR,
936 : : (errmsg("case conversion failed: %s", u_errorName(status))));
937 : 0 : return len_dest;
938 : : }
939 : :
940 : : static int32_t
941 : 0 : icu_convert_case(ICU_Convert_Func func, char *dest, size_t destsize,
942 : : const char *src, size_t srclen, pg_locale_t locale)
943 : : {
944 : : int32_t len_uchar;
945 : : int32_t len_conv;
946 : : UChar *buff_uchar;
947 : : UChar *buff_conv;
948 : : size_t result_len;
949 : :
950 : 0 : len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
951 : 0 : len_conv = convert_case_uchar(func, locale, &buff_conv,
952 : : buff_uchar, len_uchar);
953 : 0 : result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
954 : 0 : pfree(buff_uchar);
955 : 0 : pfree(buff_conv);
956 : :
957 : 0 : return result_len;
958 : : }
959 : :
960 : : static int32_t
961 : 0 : u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
962 : : const UChar *src, int32_t srcLength,
963 : : const char *locale,
964 : : UErrorCode *pErrorCode)
965 : : {
966 : 0 : return u_strToTitle(dest, destCapacity, src, srcLength,
967 : : NULL, locale, pErrorCode);
968 : : }
969 : :
970 : : static int32_t
971 : 0 : u_strFoldCase_default(UChar *dest, int32_t destCapacity,
972 : : const UChar *src, int32_t srcLength,
973 : : const char *locale,
974 : : UErrorCode *pErrorCode)
975 : : {
976 : 0 : return u_strFoldCase(dest, destCapacity, src, srcLength,
977 : 0 : foldcase_options(locale), pErrorCode);
978 : : }
979 : :
980 : : /*
981 : : * Return the correct u_strFoldCase() options for the given locale.
982 : : *
983 : : * Unlike the ICU APIs for lowercasing, titlecasing, and uppercasing, case
984 : : * folding does not accept a locale. Instead it just supports a single option
985 : : * relevant to Turkic languages 'az' and 'tr'; check for those languages.
986 : : */
987 : : static int32_t
988 : 131 : foldcase_options(const char *locale)
989 : : {
990 : 131 : uint32 options = U_FOLD_CASE_DEFAULT;
991 : : char lang[ULOC_LANG_CAPACITY];
992 : 131 : UErrorCode status = U_ZERO_ERROR;
993 : :
994 : 131 : uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &status);
995 [ + - + - ]: 131 : if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING)
996 : : {
997 : : /*
998 : : * The option name is confusing, but it causes u_strFoldCase to use
999 : : * the 'T' mappings, which are ignored for U_FOLD_CASE_DEFAULT.
1000 : : */
1001 [ + + - + ]: 131 : if (strcmp(lang, "tr") == 0 || strcmp(lang, "az") == 0)
1002 : 4 : options = U_FOLD_CASE_EXCLUDE_SPECIAL_I;
1003 : : }
1004 : :
1005 : 131 : return options;
1006 : : }
1007 : :
1008 : : /*
1009 : : * strncoll_icu
1010 : : *
1011 : : * Convert the arguments from the database encoding to UChar strings, then
1012 : : * call ucol_strcoll().
1013 : : *
1014 : : * When the database encoding is UTF-8, and ICU supports ucol_strcollUTF8(),
1015 : : * caller should call that instead.
1016 : : */
1017 : : static int
1018 : 0 : strncoll_icu_internal(const char *arg1, ssize_t len1,
1019 : : const char *arg2, ssize_t len2,
1020 : : pg_locale_t locale)
1021 : : {
1022 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
1023 : 0 : UChar *buf = sbuf;
1024 : : int32_t ulen1;
1025 : : int32_t ulen2;
1026 : : size_t bufsize;
1027 : : UChar *uchar1,
1028 : : *uchar2;
1029 : : int result;
1030 : :
1031 : : /* if encoding is UTF8, use more efficient strncoll_icu_utf8 */
1032 : : Assert(GetDatabaseEncoding() != PG_UTF8);
1033 : :
1034 : 0 : init_icu_converter();
1035 : :
1036 : 0 : ulen1 = uchar_length(icu_converter, arg1, len1);
1037 : 0 : ulen2 = uchar_length(icu_converter, arg2, len2);
1038 : :
1039 : : /* ulen1+1 or ulen2+1 doesn't risk overflow, but summing them might */
1040 : 0 : bufsize = add_size(ulen1 + 1, ulen2 + 1);
1041 [ # # ]: 0 : if (bufsize > lengthof(sbuf))
1042 : 0 : buf = palloc_array(UChar, bufsize);
1043 : :
1044 : 0 : uchar1 = buf;
1045 : 0 : uchar2 = buf + ulen1 + 1;
1046 : :
1047 : 0 : ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
1048 : 0 : ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
1049 : :
1050 : 0 : result = ucol_strcoll(locale->icu.ucol,
1051 : : uchar1, ulen1,
1052 : : uchar2, ulen2);
1053 : :
1054 [ # # ]: 0 : if (buf != sbuf)
1055 : 0 : pfree(buf);
1056 : :
1057 : 0 : return result;
1058 : : }
1059 : :
1060 : : static int
1061 : 0 : strncoll_icu(const char *arg1, size_t len1, const char *arg2, size_t len2,
1062 : : pg_locale_t locale)
1063 : : {
1064 : 0 : return strncoll_icu_internal(arg1, len1, arg2, len2, locale);
1065 : : }
1066 : :
1067 : : static int
1068 : 0 : strcoll_icu(const char *arg1, const char *arg2, pg_locale_t locale)
1069 : : {
1070 : 0 : return strncoll_icu_internal(arg1, -1, arg2, -1, locale);
1071 : : }
1072 : :
1073 : : static size_t
1074 : 0 : strnxfrm_prefix_icu_internal(char *dest, size_t destsize,
1075 : : const char *src, ssize_t srclen,
1076 : : pg_locale_t locale)
1077 : : {
1078 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
1079 : 0 : UChar *uchar = sbuf;
1080 : : UCharIterator iter;
1081 : : uint32_t state[2];
1082 : : UErrorCode status;
1083 : : int32_t ulen;
1084 : : Size result_bsize;
1085 : :
1086 : : /* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
1087 : : Assert(GetDatabaseEncoding() != PG_UTF8);
1088 : :
1089 : 0 : init_icu_converter();
1090 : :
1091 : 0 : ulen = uchar_length(icu_converter, src, srclen);
1092 : :
1093 [ # # ]: 0 : if (ulen >= lengthof(sbuf))
1094 : 0 : uchar = palloc_array(UChar, ulen + 1);
1095 : :
1096 : 0 : ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
1097 : :
1098 : 0 : uiter_setString(&iter, uchar, ulen);
1099 : 0 : state[0] = state[1] = 0; /* won't need that again */
1100 : 0 : status = U_ZERO_ERROR;
1101 : 0 : result_bsize = ucol_nextSortKeyPart(locale->icu.ucol,
1102 : : &iter,
1103 : : state,
1104 : : (uint8_t *) dest,
1105 : : destsize,
1106 : : &status);
1107 [ # # ]: 0 : if (U_FAILURE(status))
1108 [ # # ]: 0 : ereport(ERROR,
1109 : : (errmsg("sort key generation failed: %s",
1110 : : u_errorName(status))));
1111 : :
1112 [ # # ]: 0 : if (uchar != sbuf)
1113 : 0 : pfree(uchar);
1114 : :
1115 : 0 : return result_bsize;
1116 : : }
1117 : :
1118 : : static size_t
1119 : 0 : strnxfrm_prefix_icu(char *dest, size_t destsize, const char *src, size_t srclen,
1120 : : pg_locale_t locale)
1121 : : {
1122 : 0 : return strnxfrm_prefix_icu_internal(dest, destsize, src, srclen, locale);
1123 : : }
1124 : :
1125 : : static size_t
1126 : 0 : strxfrm_prefix_icu(char *dest, size_t destsize, const char *src,
1127 : : pg_locale_t locale)
1128 : : {
1129 : 0 : return strnxfrm_prefix_icu_internal(dest, destsize, src, -1, locale);
1130 : : }
1131 : :
1132 : : static void
1133 : 8340 : init_icu_converter(void)
1134 : : {
1135 : : const char *icu_encoding_name;
1136 : : UErrorCode status;
1137 : : UConverter *conv;
1138 : :
1139 [ + + ]: 8340 : if (icu_converter)
1140 : 8336 : return; /* already done */
1141 : :
1142 : 4 : icu_encoding_name = get_encoding_name_for_icu(GetDatabaseEncoding());
1143 [ - + ]: 4 : if (!icu_encoding_name)
1144 [ # # ]: 0 : ereport(ERROR,
1145 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1146 : : errmsg("encoding \"%s\" not supported by ICU",
1147 : : pg_encoding_to_char(GetDatabaseEncoding()))));
1148 : :
1149 : 4 : status = U_ZERO_ERROR;
1150 : 4 : conv = ucnv_open(icu_encoding_name, &status);
1151 [ - + ]: 4 : if (U_FAILURE(status))
1152 [ # # ]: 0 : ereport(ERROR,
1153 : : (errmsg("could not open ICU converter for encoding \"%s\": %s",
1154 : : icu_encoding_name, u_errorName(status))));
1155 : :
1156 : 4 : icu_converter = conv;
1157 : : }
1158 : :
1159 : : /*
1160 : : * Find length, in UChars, of given string if converted to UChar string.
1161 : : *
1162 : : * A length of -1 indicates that the input string is NUL-terminated.
1163 : : *
1164 : : * Note: given the assumption that the input string fits in MaxAllocSize,
1165 : : * the result cannot overflow int32_t. But callers must be careful about
1166 : : * multiplying the result by sizeof(UChar).
1167 : : */
1168 : : static int32_t
1169 : 8340 : uchar_length(UConverter *converter, const char *str, int32_t len)
1170 : : {
1171 : 8340 : UErrorCode status = U_ZERO_ERROR;
1172 : : int32_t ulen;
1173 : :
1174 : 8340 : ulen = ucnv_toUChars(converter, NULL, 0, str, len, &status);
1175 [ + - - + ]: 8340 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
1176 [ # # ]: 0 : ereport(ERROR,
1177 : : (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
1178 : 8340 : return ulen;
1179 : : }
1180 : :
1181 : : /*
1182 : : * Convert the given source string into a UChar string, stored in dest, and
1183 : : * return the length (in UChars).
1184 : : *
1185 : : * A srclen of -1 indicates that the input string is NUL-terminated.
1186 : : */
1187 : : static int32_t
1188 : 8340 : uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
1189 : : const char *src, int32_t srclen)
1190 : : {
1191 : 8340 : UErrorCode status = U_ZERO_ERROR;
1192 : : int32_t ulen;
1193 : :
1194 : 8340 : ulen = ucnv_toUChars(converter, dest, destlen, src, srclen, &status);
1195 [ - + ]: 8340 : if (U_FAILURE(status))
1196 [ # # ]: 0 : ereport(ERROR,
1197 : : (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
1198 : 8340 : return ulen;
1199 : : }
1200 : :
1201 : : /*
1202 : : * Parse collation attributes from the given locale string and apply them to
1203 : : * the open collator.
1204 : : *
1205 : : * First, the locale string is canonicalized to an ICU format locale ID such
1206 : : * as "und@colStrength=primary;colCaseLevel=yes". Then, it parses and applies
1207 : : * the key-value arguments.
1208 : : *
1209 : : * Starting with ICU version 54, the attributes are processed automatically by
1210 : : * ucol_open(), so this is only necessary for emulating this behavior on older
1211 : : * versions.
1212 : : */
1213 : : pg_attribute_unused()
1214 : : static void
1215 : 0 : icu_set_collation_attributes(UCollator *collator, const char *loc,
1216 : : UErrorCode *status)
1217 : : {
1218 : : int32_t len;
1219 : : char *icu_locale_id;
1220 : : char *lower_str;
1221 : : char *str;
1222 : : char *token;
1223 : :
1224 : : /*
1225 : : * The input locale may be a BCP 47 language tag, e.g.
1226 : : * "und-u-kc-ks-level1", which expresses the same attributes in a
1227 : : * different form. It will be converted to the equivalent ICU format
1228 : : * locale ID, e.g. "und@colcaselevel=yes;colstrength=primary", by
1229 : : * uloc_canonicalize().
1230 : : */
1231 : 0 : *status = U_ZERO_ERROR;
1232 : 0 : len = uloc_canonicalize(loc, NULL, 0, status);
1233 : 0 : icu_locale_id = palloc(len + 1);
1234 : 0 : *status = U_ZERO_ERROR;
1235 : 0 : len = uloc_canonicalize(loc, icu_locale_id, len + 1, status);
1236 [ # # # # ]: 0 : if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING)
1237 : 0 : return;
1238 : :
1239 : 0 : lower_str = asc_tolower(icu_locale_id, strlen(icu_locale_id));
1240 : :
1241 : 0 : pfree(icu_locale_id);
1242 : :
1243 : 0 : str = strchr(lower_str, '@');
1244 [ # # ]: 0 : if (!str)
1245 : 0 : return;
1246 : 0 : str++;
1247 : :
1248 [ # # ]: 0 : while ((token = strsep(&str, ";")))
1249 : : {
1250 : 0 : char *e = strchr(token, '=');
1251 : :
1252 [ # # ]: 0 : if (e)
1253 : : {
1254 : : char *name;
1255 : : char *value;
1256 : : UColAttribute uattr;
1257 : : UColAttributeValue uvalue;
1258 : :
1259 : 0 : *status = U_ZERO_ERROR;
1260 : :
1261 : 0 : *e = '\0';
1262 : 0 : name = token;
1263 : 0 : value = e + 1;
1264 : :
1265 : : /*
1266 : : * See attribute name and value lists in ICU i18n/coll.cpp
1267 : : */
1268 [ # # ]: 0 : if (strcmp(name, "colstrength") == 0)
1269 : 0 : uattr = UCOL_STRENGTH;
1270 [ # # ]: 0 : else if (strcmp(name, "colbackwards") == 0)
1271 : 0 : uattr = UCOL_FRENCH_COLLATION;
1272 [ # # ]: 0 : else if (strcmp(name, "colcaselevel") == 0)
1273 : 0 : uattr = UCOL_CASE_LEVEL;
1274 [ # # ]: 0 : else if (strcmp(name, "colcasefirst") == 0)
1275 : 0 : uattr = UCOL_CASE_FIRST;
1276 [ # # ]: 0 : else if (strcmp(name, "colalternate") == 0)
1277 : 0 : uattr = UCOL_ALTERNATE_HANDLING;
1278 [ # # ]: 0 : else if (strcmp(name, "colnormalization") == 0)
1279 : 0 : uattr = UCOL_NORMALIZATION_MODE;
1280 [ # # ]: 0 : else if (strcmp(name, "colnumeric") == 0)
1281 : 0 : uattr = UCOL_NUMERIC_COLLATION;
1282 : : else
1283 : : /* ignore if unknown */
1284 : 0 : continue;
1285 : :
1286 [ # # ]: 0 : if (strcmp(value, "primary") == 0)
1287 : 0 : uvalue = UCOL_PRIMARY;
1288 [ # # ]: 0 : else if (strcmp(value, "secondary") == 0)
1289 : 0 : uvalue = UCOL_SECONDARY;
1290 [ # # ]: 0 : else if (strcmp(value, "tertiary") == 0)
1291 : 0 : uvalue = UCOL_TERTIARY;
1292 [ # # ]: 0 : else if (strcmp(value, "quaternary") == 0)
1293 : 0 : uvalue = UCOL_QUATERNARY;
1294 [ # # ]: 0 : else if (strcmp(value, "identical") == 0)
1295 : 0 : uvalue = UCOL_IDENTICAL;
1296 [ # # ]: 0 : else if (strcmp(value, "no") == 0)
1297 : 0 : uvalue = UCOL_OFF;
1298 [ # # ]: 0 : else if (strcmp(value, "yes") == 0)
1299 : 0 : uvalue = UCOL_ON;
1300 [ # # ]: 0 : else if (strcmp(value, "shifted") == 0)
1301 : 0 : uvalue = UCOL_SHIFTED;
1302 [ # # ]: 0 : else if (strcmp(value, "non-ignorable") == 0)
1303 : 0 : uvalue = UCOL_NON_IGNORABLE;
1304 [ # # ]: 0 : else if (strcmp(value, "lower") == 0)
1305 : 0 : uvalue = UCOL_LOWER_FIRST;
1306 [ # # ]: 0 : else if (strcmp(value, "upper") == 0)
1307 : 0 : uvalue = UCOL_UPPER_FIRST;
1308 : : else
1309 : : {
1310 : 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1311 : 0 : break;
1312 : : }
1313 : :
1314 : 0 : ucol_setAttribute(collator, uattr, uvalue, status);
1315 : : }
1316 : : }
1317 : :
1318 : 0 : pfree(lower_str);
1319 : : }
1320 : :
1321 : : #endif /* USE_ICU */
|