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 : 141 : create_pg_locale_icu(Oid collid, MemoryContext context)
314 : : {
315 : : #ifdef USE_ICU
316 : : bool deterministic;
317 : : const char *iculocstr;
318 : 141 : const char *icurules = NULL;
319 : : UCollator *collator;
320 : 141 : locale_t loc = (locale_t) 0;
321 : : pg_locale_t result;
322 : :
323 [ + + ]: 141 : 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 : 128 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
365 [ - + ]: 128 : if (!HeapTupleIsValid(tp))
366 [ # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
367 : 128 : collform = (Form_pg_collation) GETSTRUCT(tp);
368 : 128 : deterministic = collform->collisdeterministic;
369 : 128 : datum = SysCacheGetAttrNotNull(COLLOID, tp,
370 : : Anum_pg_collation_colllocale);
371 : 128 : iculocstr = TextDatumGetCString(datum);
372 : 128 : datum = SysCacheGetAttr(COLLOID, tp,
373 : : Anum_pg_collation_collicurules, &isnull);
374 [ + + ]: 128 : if (!isnull)
375 : 12 : icurules = TextDatumGetCString(datum);
376 : :
377 : 128 : ReleaseSysCache(tp);
378 : : }
379 : :
380 : 141 : collator = make_icu_collator(iculocstr, icurules);
381 : :
382 : 135 : result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
383 : 135 : result->icu.locale = MemoryContextStrdup(context, iculocstr);
384 : 135 : result->icu.ucol = collator;
385 : 135 : result->icu.lt = loc;
386 : 135 : result->deterministic = deterministic;
387 : 135 : result->collate_is_c = false;
388 : 135 : result->ctype_is_c = false;
389 [ + - ]: 135 : if (GetDatabaseEncoding() == PG_UTF8)
390 : : {
391 : 135 : result->icu.ucasemap = pg_ucasemap_open(iculocstr);
392 : 135 : result->collate = &collate_methods_icu_utf8;
393 : 135 : 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 : 135 : 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 : 47653 : 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 [ - + ]: 47653 : 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 : 47653 : 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 : 47518 : pg_ucol_open(const char *loc_str)
448 : : {
449 : : UCollator *collator;
450 : : UErrorCode status;
451 : : char *fixed_str;
452 : :
453 : 47518 : fixed_str = fix_icu_locale_str(loc_str);
454 : :
455 : 47518 : status = U_ZERO_ERROR;
456 : 47518 : collator = ucol_open(fixed_str, &status);
457 [ + + ]: 47518 : 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 : 47511 : pfree(fixed_str);
465 : :
466 : 47511 : 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 : 135 : pg_ucasemap_open(const char *loc_str)
477 : : {
478 : 135 : UErrorCode status = U_ZERO_ERROR;
479 : : UCaseMap *casemap;
480 : : char *fixed_str;
481 : :
482 : 135 : fixed_str = fix_icu_locale_str(loc_str);
483 : :
484 : 135 : casemap = ucasemap_open(fixed_str, foldcase_options(fixed_str), &status);
485 [ - + ]: 135 : 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 : 135 : pfree(fixed_str);
493 : :
494 : 135 : 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 : 141 : make_icu_collator(const char *iculocstr, const char *icurules)
504 : : {
505 [ + + ]: 141 : if (!icurules)
506 : : {
507 : : /* simple case without rules */
508 : 129 : 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 : 372 : strlower_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
597 : : pg_locale_t locale)
598 : : {
599 : 372 : UErrorCode status = U_ZERO_ERROR;
600 : : int32_t needed;
601 : :
602 : 372 : needed = ucasemap_utf8ToLower(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
603 [ + + - + ]: 372 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
604 [ # # ]: 0 : ereport(ERROR,
605 : : errmsg("case conversion failed: %s", u_errorName(status)));
606 : 372 : return needed;
607 : : }
608 : :
609 : : static size_t
610 : 24 : strtitle_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
611 : : pg_locale_t locale)
612 : : {
613 : 24 : UErrorCode status = U_ZERO_ERROR;
614 : : int32_t needed;
615 : :
616 : 24 : needed = ucasemap_utf8ToTitle(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
617 [ - + - - ]: 24 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
618 [ # # ]: 0 : ereport(ERROR,
619 : : errmsg("case conversion failed: %s", u_errorName(status)));
620 : 24 : return needed;
621 : : }
622 : :
623 : : static size_t
624 : 80 : strupper_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
625 : : pg_locale_t locale)
626 : : {
627 : 80 : UErrorCode status = U_ZERO_ERROR;
628 : : int32_t needed;
629 : :
630 : 80 : needed = ucasemap_utf8ToUpper(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
631 [ + + - + ]: 80 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
632 [ # # ]: 0 : ereport(ERROR,
633 : : errmsg("case conversion failed: %s", u_errorName(status)));
634 : 80 : return needed;
635 : : }
636 : :
637 : : static size_t
638 : 14 : strfold_icu_utf8(char *dest, size_t destsize, const char *src, size_t srclen,
639 : : pg_locale_t locale)
640 : : {
641 : 14 : UErrorCode status = U_ZERO_ERROR;
642 : : int32_t needed;
643 : :
644 : 14 : needed = ucasemap_utf8FoldCase(locale->icu.ucasemap, dest, destsize, src, srclen, &status);
645 [ - + - - ]: 14 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
646 [ # # ]: 0 : ereport(ERROR,
647 : : errmsg("case conversion failed: %s", u_errorName(status)));
648 : 14 : return needed;
649 : : }
650 : :
651 : : /*
652 : : * For historical compatibility, behavior is not multibyte-aware.
653 : : *
654 : : * NB: uses libc tolower_l() for single-byte encodings (also for historical
655 : : * compatibility), and therefore relies on the 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 : : * Wrapper for ucol_strcollUTF8().
688 : : */
689 : : int
690 : 17956 : strncoll_icu_utf8(const char *arg1, size_t len1, const char *arg2, size_t len2,
691 : : pg_locale_t locale)
692 : : {
693 : : int result;
694 : : UErrorCode status;
695 : :
696 : : Assert(GetDatabaseEncoding() == PG_UTF8);
697 : :
698 : 17956 : status = U_ZERO_ERROR;
699 : 17956 : result = ucol_strcollUTF8(locale->icu.ucol,
700 : : arg1, len1,
701 : : arg2, len2,
702 : : &status);
703 [ - + ]: 17956 : if (U_FAILURE(status))
704 [ # # ]: 0 : ereport(ERROR,
705 : : (errmsg("collation failed: %s", u_errorName(status))));
706 : :
707 : 17956 : return result;
708 : : }
709 : :
710 : : int
711 : 1148 : strcoll_icu_utf8(const char *arg1, const char *arg2, pg_locale_t locale)
712 : : {
713 : : int result;
714 : : UErrorCode status;
715 : :
716 : : Assert(GetDatabaseEncoding() == PG_UTF8);
717 : :
718 : 1148 : status = U_ZERO_ERROR;
719 : 1148 : result = ucol_strcollUTF8(locale->icu.ucol,
720 : : arg1, -1,
721 : : arg2, -1,
722 : : &status);
723 [ - + ]: 1148 : if (U_FAILURE(status))
724 [ # # ]: 0 : ereport(ERROR,
725 : : (errmsg("collation failed: %s", u_errorName(status))));
726 : :
727 : 1148 : return result;
728 : : }
729 : :
730 : : static size_t
731 : 8336 : strnxfrm_icu_internal(char *dest, size_t destsize, const char *src, ssize_t srclen,
732 : : pg_locale_t locale)
733 : : {
734 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
735 : 8336 : UChar *uchar = sbuf;
736 : : int32_t ulen;
737 : : Size result_bsize;
738 : :
739 : 8336 : init_icu_converter();
740 : :
741 : 8336 : ulen = uchar_length(icu_converter, src, srclen);
742 : :
743 [ - + ]: 8336 : if (ulen >= lengthof(sbuf))
744 : 0 : uchar = palloc_array(UChar, ulen + 1);
745 : :
746 : 8336 : ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
747 : :
748 : 8336 : result_bsize = ucol_getSortKey(locale->icu.ucol,
749 : : uchar, ulen,
750 : : (uint8_t *) dest, destsize);
751 : :
752 : : /*
753 : : * ucol_getSortKey() counts the nul-terminator in the result length, but
754 : : * this function should not.
755 : : */
756 : : Assert(result_bsize > 0);
757 : 8336 : result_bsize--;
758 : :
759 [ - + ]: 8336 : if (uchar != sbuf)
760 : 0 : pfree(uchar);
761 : :
762 : : /* if dest is defined, it should be nul-terminated */
763 : : Assert(result_bsize >= destsize || dest[result_bsize] == '\0');
764 : :
765 : 8336 : return result_bsize;
766 : : }
767 : :
768 : : static size_t
769 : 8336 : strnxfrm_icu(char *dest, size_t destsize, const char *src, size_t srclen,
770 : : pg_locale_t locale)
771 : : {
772 : 8336 : return strnxfrm_icu_internal(dest, destsize, src, srclen, locale);
773 : : }
774 : :
775 : : static size_t
776 : 0 : strxfrm_icu(char *dest, size_t destsize, const char *src,
777 : : pg_locale_t locale)
778 : : {
779 : 0 : return strnxfrm_icu_internal(dest, destsize, src, -1, locale);
780 : : }
781 : :
782 : : static size_t
783 : 1306 : strnxfrm_prefix_icu_utf8_internal(char *dest, size_t destsize,
784 : : const char *src, ssize_t srclen,
785 : : pg_locale_t locale)
786 : : {
787 : : size_t result;
788 : : UCharIterator iter;
789 : : uint32_t state[2];
790 : : UErrorCode status;
791 : :
792 : : Assert(GetDatabaseEncoding() == PG_UTF8);
793 : :
794 : 1306 : uiter_setUTF8(&iter, src, srclen);
795 : 1306 : state[0] = state[1] = 0; /* won't need that again */
796 : 1306 : status = U_ZERO_ERROR;
797 : 1306 : result = ucol_nextSortKeyPart(locale->icu.ucol,
798 : : &iter,
799 : : state,
800 : : (uint8_t *) dest,
801 : : destsize,
802 : : &status);
803 [ - + ]: 1306 : if (U_FAILURE(status))
804 [ # # ]: 0 : ereport(ERROR,
805 : : (errmsg("sort key generation failed: %s",
806 : : u_errorName(status))));
807 : :
808 : 1306 : return result;
809 : : }
810 : :
811 : : static size_t
812 : 4 : strnxfrm_prefix_icu_utf8(char *dest, size_t destsize,
813 : : const char *src, size_t srclen,
814 : : pg_locale_t locale)
815 : : {
816 : 4 : return strnxfrm_prefix_icu_utf8_internal(dest, destsize, src, srclen, locale);
817 : : }
818 : :
819 : : static size_t
820 : 1302 : strxfrm_prefix_icu_utf8(char *dest, size_t destsize, const char *src,
821 : : pg_locale_t locale)
822 : : {
823 : 1302 : return strnxfrm_prefix_icu_utf8_internal(dest, destsize, src, -1, locale);
824 : : }
825 : :
826 : : char *
827 : 47280 : get_collation_actual_version_icu(const char *collcollate)
828 : : {
829 : : UCollator *collator;
830 : : UVersionInfo versioninfo;
831 : : char buf[U_MAX_VERSION_STRING_LENGTH];
832 : :
833 : 47280 : collator = pg_ucol_open(collcollate);
834 : :
835 : 47280 : ucol_getVersion(collator, versioninfo);
836 : 47280 : ucol_close(collator);
837 : :
838 : 47280 : u_versionToString(versioninfo, buf);
839 : 47280 : return pstrdup(buf);
840 : : }
841 : :
842 : : /*
843 : : * Convert a string in the database encoding into a string of UChars.
844 : : *
845 : : * The source string at buff is of length nbytes
846 : : * (it needn't be nul-terminated)
847 : : *
848 : : * *buff_uchar receives a pointer to the palloc'd result string, and
849 : : * the function's result is the number of UChars generated.
850 : : *
851 : : * The result string is nul-terminated, though most callers rely on the
852 : : * result length instead.
853 : : */
854 : : static int32_t
855 : 12 : icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
856 : : {
857 : : int32_t len_uchar;
858 : :
859 : 12 : init_icu_converter();
860 : :
861 : 12 : len_uchar = uchar_length(icu_converter, buff, nbytes);
862 : :
863 : 12 : *buff_uchar = palloc_array(UChar, len_uchar + 1);
864 : 12 : len_uchar = uchar_convert(icu_converter,
865 : : *buff_uchar, len_uchar + 1, buff, nbytes);
866 : :
867 : 12 : return len_uchar;
868 : : }
869 : :
870 : : /*
871 : : * Convert a string of UChars into the database encoding.
872 : : *
873 : : * The source string at buff_uchar is of length len_uchar (it needn't be
874 : : * nul-terminated)
875 : : *
876 : : * If the result length is less than destsize, the NUL-terminated result is
877 : : * stored in dest. Otherwise the contents of dest are undefined.
878 : : */
879 : : static size_t
880 : 0 : icu_from_uchar(char *dest, size_t destsize, const UChar *buff_uchar, int32_t len_uchar)
881 : : {
882 : : UErrorCode status;
883 : : int32_t len_result;
884 : :
885 : 0 : init_icu_converter();
886 : :
887 : 0 : status = U_ZERO_ERROR;
888 : 0 : len_result = ucnv_fromUChars(icu_converter, NULL, 0,
889 : : buff_uchar, len_uchar, &status);
890 [ # # # # ]: 0 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
891 [ # # ]: 0 : ereport(ERROR,
892 : : (errmsg("%s failed: %s", "ucnv_fromUChars",
893 : : u_errorName(status))));
894 : :
895 [ # # ]: 0 : if (len_result + 1 > destsize)
896 : 0 : return len_result;
897 : :
898 : 0 : status = U_ZERO_ERROR;
899 : 0 : len_result = ucnv_fromUChars(icu_converter, dest, len_result + 1,
900 : : buff_uchar, len_uchar, &status);
901 [ # # ]: 0 : if (U_FAILURE(status) ||
902 [ # # ]: 0 : status == U_STRING_NOT_TERMINATED_WARNING)
903 [ # # ]: 0 : ereport(ERROR,
904 : : (errmsg("%s failed: %s", "ucnv_fromUChars",
905 : : u_errorName(status))));
906 : :
907 : 0 : return len_result;
908 : : }
909 : :
910 : : static int32_t
911 : 0 : convert_case_uchar(ICU_Convert_Func func, pg_locale_t mylocale,
912 : : UChar **buff_dest, UChar *buff_source, int32_t len_source)
913 : : {
914 : : UErrorCode status;
915 : : int32_t len_dest;
916 : :
917 : 0 : len_dest = len_source; /* try first with same length */
918 : 0 : *buff_dest = palloc_array(UChar, len_dest);
919 : 0 : status = U_ZERO_ERROR;
920 : 0 : len_dest = func(*buff_dest, len_dest, buff_source, len_source,
921 : : mylocale->icu.locale, &status);
922 [ # # ]: 0 : if (status == U_BUFFER_OVERFLOW_ERROR)
923 : : {
924 : : /* try again with adjusted length */
925 : 0 : pfree(*buff_dest);
926 : 0 : *buff_dest = palloc_array(UChar, len_dest);
927 : 0 : status = U_ZERO_ERROR;
928 : 0 : len_dest = func(*buff_dest, len_dest, buff_source, len_source,
929 : : mylocale->icu.locale, &status);
930 : : }
931 [ # # ]: 0 : if (U_FAILURE(status))
932 [ # # ]: 0 : ereport(ERROR,
933 : : (errmsg("case conversion failed: %s", u_errorName(status))));
934 : 0 : return len_dest;
935 : : }
936 : :
937 : : static int32_t
938 : 0 : icu_convert_case(ICU_Convert_Func func, char *dest, size_t destsize,
939 : : const char *src, size_t srclen, pg_locale_t locale)
940 : : {
941 : : int32_t len_uchar;
942 : : int32_t len_conv;
943 : : UChar *buff_uchar;
944 : : UChar *buff_conv;
945 : : size_t result_len;
946 : :
947 : 0 : len_uchar = icu_to_uchar(&buff_uchar, src, srclen);
948 : 0 : len_conv = convert_case_uchar(func, locale, &buff_conv,
949 : : buff_uchar, len_uchar);
950 : 0 : result_len = icu_from_uchar(dest, destsize, buff_conv, len_conv);
951 : 0 : pfree(buff_uchar);
952 : 0 : pfree(buff_conv);
953 : :
954 : 0 : return result_len;
955 : : }
956 : :
957 : : static int32_t
958 : 0 : u_strToTitle_default_BI(UChar *dest, int32_t destCapacity,
959 : : const UChar *src, int32_t srcLength,
960 : : const char *locale,
961 : : UErrorCode *pErrorCode)
962 : : {
963 : 0 : return u_strToTitle(dest, destCapacity, src, srcLength,
964 : : NULL, locale, pErrorCode);
965 : : }
966 : :
967 : : static int32_t
968 : 0 : u_strFoldCase_default(UChar *dest, int32_t destCapacity,
969 : : const UChar *src, int32_t srcLength,
970 : : const char *locale,
971 : : UErrorCode *pErrorCode)
972 : : {
973 : 0 : return u_strFoldCase(dest, destCapacity, src, srcLength,
974 : 0 : foldcase_options(locale), pErrorCode);
975 : : }
976 : :
977 : : /*
978 : : * Return the correct u_strFoldCase() options for the given locale.
979 : : *
980 : : * Unlike the ICU APIs for lowercasing, titlecasing, and uppercasing, case
981 : : * folding does not accept a locale. Instead it just supports a single option
982 : : * relevant to Turkic languages 'az' and 'tr'; check for those languages.
983 : : */
984 : : static int32_t
985 : 135 : foldcase_options(const char *locale)
986 : : {
987 : 135 : uint32 options = U_FOLD_CASE_DEFAULT;
988 : : char lang[ULOC_LANG_CAPACITY];
989 : 135 : UErrorCode status = U_ZERO_ERROR;
990 : :
991 : 135 : uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &status);
992 [ + - + - ]: 135 : if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING)
993 : : {
994 : : /*
995 : : * The option name is confusing, but it causes u_strFoldCase to use
996 : : * the 'T' mappings, which are ignored for U_FOLD_CASE_DEFAULT.
997 : : */
998 [ + + - + ]: 135 : if (strcmp(lang, "tr") == 0 || strcmp(lang, "az") == 0)
999 : 4 : options = U_FOLD_CASE_EXCLUDE_SPECIAL_I;
1000 : : }
1001 : :
1002 : 135 : return options;
1003 : : }
1004 : :
1005 : : /*
1006 : : * strncoll_icu
1007 : : *
1008 : : * Convert the arguments from the database encoding to UChar strings, then
1009 : : * call ucol_strcoll().
1010 : : *
1011 : : * When the database encoding is UTF-8, and ICU supports ucol_strcollUTF8(),
1012 : : * caller should call that instead.
1013 : : */
1014 : : static int
1015 : 0 : strncoll_icu_internal(const char *arg1, ssize_t len1,
1016 : : const char *arg2, ssize_t len2,
1017 : : pg_locale_t locale)
1018 : : {
1019 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
1020 : 0 : UChar *buf = sbuf;
1021 : : int32_t ulen1;
1022 : : int32_t ulen2;
1023 : : size_t bufsize;
1024 : : UChar *uchar1,
1025 : : *uchar2;
1026 : : int result;
1027 : :
1028 : : /* if encoding is UTF8, use more efficient strncoll_icu_utf8 */
1029 : : Assert(GetDatabaseEncoding() != PG_UTF8);
1030 : :
1031 : 0 : init_icu_converter();
1032 : :
1033 : 0 : ulen1 = uchar_length(icu_converter, arg1, len1);
1034 : 0 : ulen2 = uchar_length(icu_converter, arg2, len2);
1035 : :
1036 : : /* ulen1+1 or ulen2+1 doesn't risk overflow, but summing them might */
1037 : 0 : bufsize = add_size(ulen1 + 1, ulen2 + 1);
1038 [ # # ]: 0 : if (bufsize > lengthof(sbuf))
1039 : 0 : buf = palloc_array(UChar, bufsize);
1040 : :
1041 : 0 : uchar1 = buf;
1042 : 0 : uchar2 = buf + ulen1 + 1;
1043 : :
1044 : 0 : ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
1045 : 0 : ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
1046 : :
1047 : 0 : result = ucol_strcoll(locale->icu.ucol,
1048 : : uchar1, ulen1,
1049 : : uchar2, ulen2);
1050 : :
1051 [ # # ]: 0 : if (buf != sbuf)
1052 : 0 : pfree(buf);
1053 : :
1054 : 0 : return result;
1055 : : }
1056 : :
1057 : : static int
1058 : 0 : strncoll_icu(const char *arg1, size_t len1, const char *arg2, size_t len2,
1059 : : pg_locale_t locale)
1060 : : {
1061 : 0 : return strncoll_icu_internal(arg1, len1, arg2, len2, locale);
1062 : : }
1063 : :
1064 : : static int
1065 : 0 : strcoll_icu(const char *arg1, const char *arg2, pg_locale_t locale)
1066 : : {
1067 : 0 : return strncoll_icu_internal(arg1, -1, arg2, -1, locale);
1068 : : }
1069 : :
1070 : : static size_t
1071 : 0 : strnxfrm_prefix_icu_internal(char *dest, size_t destsize,
1072 : : const char *src, ssize_t srclen,
1073 : : pg_locale_t locale)
1074 : : {
1075 : : UChar sbuf[TEXTBUFLEN / sizeof(UChar)];
1076 : 0 : UChar *uchar = sbuf;
1077 : : UCharIterator iter;
1078 : : uint32_t state[2];
1079 : : UErrorCode status;
1080 : : int32_t ulen;
1081 : : Size result_bsize;
1082 : :
1083 : : /* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
1084 : : Assert(GetDatabaseEncoding() != PG_UTF8);
1085 : :
1086 : 0 : init_icu_converter();
1087 : :
1088 : 0 : ulen = uchar_length(icu_converter, src, srclen);
1089 : :
1090 [ # # ]: 0 : if (ulen >= lengthof(sbuf))
1091 : 0 : uchar = palloc_array(UChar, ulen + 1);
1092 : :
1093 : 0 : ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
1094 : :
1095 : 0 : uiter_setString(&iter, uchar, ulen);
1096 : 0 : state[0] = state[1] = 0; /* won't need that again */
1097 : 0 : status = U_ZERO_ERROR;
1098 : 0 : result_bsize = ucol_nextSortKeyPart(locale->icu.ucol,
1099 : : &iter,
1100 : : state,
1101 : : (uint8_t *) dest,
1102 : : destsize,
1103 : : &status);
1104 [ # # ]: 0 : if (U_FAILURE(status))
1105 [ # # ]: 0 : ereport(ERROR,
1106 : : (errmsg("sort key generation failed: %s",
1107 : : u_errorName(status))));
1108 : :
1109 [ # # ]: 0 : if (uchar != sbuf)
1110 : 0 : pfree(uchar);
1111 : :
1112 : 0 : return result_bsize;
1113 : : }
1114 : :
1115 : : static size_t
1116 : 0 : strnxfrm_prefix_icu(char *dest, size_t destsize, const char *src, size_t srclen,
1117 : : pg_locale_t locale)
1118 : : {
1119 : 0 : return strnxfrm_prefix_icu_internal(dest, destsize, src, srclen, locale);
1120 : : }
1121 : :
1122 : : static size_t
1123 : 0 : strxfrm_prefix_icu(char *dest, size_t destsize, const char *src,
1124 : : pg_locale_t locale)
1125 : : {
1126 : 0 : return strnxfrm_prefix_icu_internal(dest, destsize, src, -1, locale);
1127 : : }
1128 : :
1129 : : static void
1130 : 8348 : init_icu_converter(void)
1131 : : {
1132 : : const char *icu_encoding_name;
1133 : : UErrorCode status;
1134 : : UConverter *conv;
1135 : :
1136 [ + + ]: 8348 : if (icu_converter)
1137 : 8340 : return; /* already done */
1138 : :
1139 : 8 : icu_encoding_name = get_encoding_name_for_icu(GetDatabaseEncoding());
1140 [ - + ]: 8 : if (!icu_encoding_name)
1141 [ # # ]: 0 : ereport(ERROR,
1142 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1143 : : errmsg("encoding \"%s\" not supported by ICU",
1144 : : pg_encoding_to_char(GetDatabaseEncoding()))));
1145 : :
1146 : 8 : status = U_ZERO_ERROR;
1147 : 8 : conv = ucnv_open(icu_encoding_name, &status);
1148 [ - + ]: 8 : if (U_FAILURE(status))
1149 [ # # ]: 0 : ereport(ERROR,
1150 : : (errmsg("could not open ICU converter for encoding \"%s\": %s",
1151 : : icu_encoding_name, u_errorName(status))));
1152 : :
1153 : 8 : icu_converter = conv;
1154 : : }
1155 : :
1156 : : /*
1157 : : * Find length, in UChars, of given string if converted to UChar string.
1158 : : *
1159 : : * A length of -1 indicates that the input string is NUL-terminated.
1160 : : *
1161 : : * Note: given the assumption that the input string fits in MaxAllocSize,
1162 : : * the result cannot overflow int32_t. But callers must be careful about
1163 : : * multiplying the result by sizeof(UChar).
1164 : : */
1165 : : static int32_t
1166 : 8348 : uchar_length(UConverter *converter, const char *str, int32_t len)
1167 : : {
1168 : 8348 : UErrorCode status = U_ZERO_ERROR;
1169 : : int32_t ulen;
1170 : :
1171 : 8348 : ulen = ucnv_toUChars(converter, NULL, 0, str, len, &status);
1172 [ + - - + ]: 8348 : if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
1173 [ # # ]: 0 : ereport(ERROR,
1174 : : (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
1175 : 8348 : return ulen;
1176 : : }
1177 : :
1178 : : /*
1179 : : * Convert the given source string into a UChar string, stored in dest, and
1180 : : * return the length (in UChars).
1181 : : *
1182 : : * A srclen of -1 indicates that the input string is NUL-terminated.
1183 : : */
1184 : : static int32_t
1185 : 8348 : uchar_convert(UConverter *converter, UChar *dest, int32_t destlen,
1186 : : const char *src, int32_t srclen)
1187 : : {
1188 : 8348 : UErrorCode status = U_ZERO_ERROR;
1189 : : int32_t ulen;
1190 : :
1191 : 8348 : ulen = ucnv_toUChars(converter, dest, destlen, src, srclen, &status);
1192 [ - + ]: 8348 : if (U_FAILURE(status))
1193 [ # # ]: 0 : ereport(ERROR,
1194 : : (errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
1195 : 8348 : return ulen;
1196 : : }
1197 : :
1198 : : /*
1199 : : * Parse collation attributes from the given locale string and apply them to
1200 : : * the open collator.
1201 : : *
1202 : : * First, the locale string is canonicalized to an ICU format locale ID such
1203 : : * as "und@colStrength=primary;colCaseLevel=yes". Then, it parses and applies
1204 : : * the key-value arguments.
1205 : : *
1206 : : * Starting with ICU version 54, the attributes are processed automatically by
1207 : : * ucol_open(), so this is only necessary for emulating this behavior on older
1208 : : * versions.
1209 : : */
1210 : : pg_attribute_unused()
1211 : : static void
1212 : 0 : icu_set_collation_attributes(UCollator *collator, const char *loc,
1213 : : UErrorCode *status)
1214 : : {
1215 : : int32_t len;
1216 : : char *icu_locale_id;
1217 : : char *lower_str;
1218 : : char *str;
1219 : : char *token;
1220 : :
1221 : : /*
1222 : : * The input locale may be a BCP 47 language tag, e.g.
1223 : : * "und-u-kc-ks-level1", which expresses the same attributes in a
1224 : : * different form. It will be converted to the equivalent ICU format
1225 : : * locale ID, e.g. "und@colcaselevel=yes;colstrength=primary", by
1226 : : * uloc_canonicalize().
1227 : : */
1228 : 0 : *status = U_ZERO_ERROR;
1229 : 0 : len = uloc_canonicalize(loc, NULL, 0, status);
1230 : 0 : icu_locale_id = palloc(len + 1);
1231 : 0 : *status = U_ZERO_ERROR;
1232 : 0 : len = uloc_canonicalize(loc, icu_locale_id, len + 1, status);
1233 [ # # # # ]: 0 : if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING)
1234 : 0 : return;
1235 : :
1236 : 0 : lower_str = asc_tolower(icu_locale_id, strlen(icu_locale_id));
1237 : :
1238 : 0 : pfree(icu_locale_id);
1239 : :
1240 : 0 : str = strchr(lower_str, '@');
1241 [ # # ]: 0 : if (!str)
1242 : 0 : return;
1243 : 0 : str++;
1244 : :
1245 [ # # ]: 0 : while ((token = strsep(&str, ";")))
1246 : : {
1247 : 0 : char *e = strchr(token, '=');
1248 : :
1249 [ # # ]: 0 : if (e)
1250 : : {
1251 : : char *name;
1252 : : char *value;
1253 : : UColAttribute uattr;
1254 : : UColAttributeValue uvalue;
1255 : :
1256 : 0 : *status = U_ZERO_ERROR;
1257 : :
1258 : 0 : *e = '\0';
1259 : 0 : name = token;
1260 : 0 : value = e + 1;
1261 : :
1262 : : /*
1263 : : * See attribute name and value lists in ICU i18n/coll.cpp
1264 : : */
1265 [ # # ]: 0 : if (strcmp(name, "colstrength") == 0)
1266 : 0 : uattr = UCOL_STRENGTH;
1267 [ # # ]: 0 : else if (strcmp(name, "colbackwards") == 0)
1268 : 0 : uattr = UCOL_FRENCH_COLLATION;
1269 [ # # ]: 0 : else if (strcmp(name, "colcaselevel") == 0)
1270 : 0 : uattr = UCOL_CASE_LEVEL;
1271 [ # # ]: 0 : else if (strcmp(name, "colcasefirst") == 0)
1272 : 0 : uattr = UCOL_CASE_FIRST;
1273 [ # # ]: 0 : else if (strcmp(name, "colalternate") == 0)
1274 : 0 : uattr = UCOL_ALTERNATE_HANDLING;
1275 [ # # ]: 0 : else if (strcmp(name, "colnormalization") == 0)
1276 : 0 : uattr = UCOL_NORMALIZATION_MODE;
1277 [ # # ]: 0 : else if (strcmp(name, "colnumeric") == 0)
1278 : 0 : uattr = UCOL_NUMERIC_COLLATION;
1279 : : else
1280 : : /* ignore if unknown */
1281 : 0 : continue;
1282 : :
1283 [ # # ]: 0 : if (strcmp(value, "primary") == 0)
1284 : 0 : uvalue = UCOL_PRIMARY;
1285 [ # # ]: 0 : else if (strcmp(value, "secondary") == 0)
1286 : 0 : uvalue = UCOL_SECONDARY;
1287 [ # # ]: 0 : else if (strcmp(value, "tertiary") == 0)
1288 : 0 : uvalue = UCOL_TERTIARY;
1289 [ # # ]: 0 : else if (strcmp(value, "quaternary") == 0)
1290 : 0 : uvalue = UCOL_QUATERNARY;
1291 [ # # ]: 0 : else if (strcmp(value, "identical") == 0)
1292 : 0 : uvalue = UCOL_IDENTICAL;
1293 [ # # ]: 0 : else if (strcmp(value, "no") == 0)
1294 : 0 : uvalue = UCOL_OFF;
1295 [ # # ]: 0 : else if (strcmp(value, "yes") == 0)
1296 : 0 : uvalue = UCOL_ON;
1297 [ # # ]: 0 : else if (strcmp(value, "shifted") == 0)
1298 : 0 : uvalue = UCOL_SHIFTED;
1299 [ # # ]: 0 : else if (strcmp(value, "non-ignorable") == 0)
1300 : 0 : uvalue = UCOL_NON_IGNORABLE;
1301 [ # # ]: 0 : else if (strcmp(value, "lower") == 0)
1302 : 0 : uvalue = UCOL_LOWER_FIRST;
1303 [ # # ]: 0 : else if (strcmp(value, "upper") == 0)
1304 : 0 : uvalue = UCOL_UPPER_FIRST;
1305 : : else
1306 : : {
1307 : 0 : *status = U_ILLEGAL_ARGUMENT_ERROR;
1308 : 0 : break;
1309 : : }
1310 : :
1311 : 0 : ucol_setAttribute(collator, uattr, uvalue, status);
1312 : : }
1313 : : }
1314 : :
1315 : 0 : pfree(lower_str);
1316 : : }
1317 : :
1318 : : #endif /* USE_ICU */
|