Branch data Line data Source code
1 : : /*-----------------------------------------------------------------------
2 : : *
3 : : * PostgreSQL locale utilities
4 : : *
5 : : * Portions Copyright (c) 2002-2026, PostgreSQL Global Development Group
6 : : *
7 : : * src/backend/utils/adt/pg_locale.c
8 : : *
9 : : *-----------------------------------------------------------------------
10 : : */
11 : :
12 : : /*----------
13 : : * Here is how the locale stuff is handled: LC_COLLATE and LC_CTYPE
14 : : * are fixed at CREATE DATABASE time, stored in pg_database, and cannot
15 : : * be changed. Thus, the effects of strcoll(), strxfrm(), isupper(),
16 : : * toupper(), etc. are always in the same fixed locale.
17 : : *
18 : : * LC_MESSAGES is settable at run time and will take effect
19 : : * immediately.
20 : : *
21 : : * The other categories, LC_MONETARY, LC_NUMERIC, and LC_TIME are
22 : : * permanently set to "C", and then we use temporary locale_t
23 : : * objects when we need to look up locale data based on the GUCs
24 : : * of the same name. Information is cached when the GUCs change.
25 : : * The cached information is only used by the formatting functions
26 : : * (to_char, etc.) and the money type. For the user, this should all be
27 : : * transparent.
28 : : *----------
29 : : */
30 : :
31 : :
32 : : #include "postgres.h"
33 : :
34 : : #include <time.h>
35 : : #ifdef USE_ICU
36 : : #include <unicode/ucol.h>
37 : : #endif
38 : :
39 : : #include "access/htup_details.h"
40 : : #include "catalog/pg_collation.h"
41 : : #include "catalog/pg_database.h"
42 : : #include "common/hashfn.h"
43 : : #include "common/string.h"
44 : : #include "mb/pg_wchar.h"
45 : : #include "miscadmin.h"
46 : : #include "utils/builtins.h"
47 : : #include "utils/guc_hooks.h"
48 : : #include "utils/lsyscache.h"
49 : : #include "utils/memutils.h"
50 : : #include "utils/pg_locale.h"
51 : : #include "utils/pg_locale_c.h"
52 : : #include "utils/relcache.h"
53 : : #include "utils/syscache.h"
54 : :
55 : : #ifdef WIN32
56 : : #include <shlwapi.h>
57 : : #endif
58 : :
59 : : /* Error triggered for locale-sensitive subroutines */
60 : : #define PGLOCALE_SUPPORT_ERROR(provider) \
61 : : elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
62 : :
63 : : /*
64 : : * This should be large enough that most strings will fit, but small enough
65 : : * that we feel comfortable putting it on the stack
66 : : */
67 : : #define TEXTBUFLEN 1024
68 : :
69 : : #define MAX_L10N_DATA 80
70 : :
71 : : /* pg_locale_builtin.c */
72 : : extern pg_locale_t create_pg_locale_builtin(Oid collid, MemoryContext context);
73 : : extern char *get_collation_actual_version_builtin(const char *collcollate);
74 : :
75 : : /* pg_locale_icu.c */
76 : : #ifdef USE_ICU
77 : : extern UCollator *pg_ucol_open(const char *loc_str);
78 : : extern char *get_collation_actual_version_icu(const char *collcollate);
79 : : #endif
80 : : extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
81 : :
82 : : /* pg_locale_libc.c */
83 : : extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
84 : : extern char *get_collation_actual_version_libc(const char *collcollate);
85 : :
86 : : /* GUC settings */
87 : : char *locale_messages;
88 : : char *locale_monetary;
89 : : char *locale_numeric;
90 : : char *locale_time;
91 : :
92 : : int icu_validation_level = WARNING;
93 : :
94 : : /*
95 : : * lc_time localization cache.
96 : : *
97 : : * We use only the first 7 or 12 entries of these arrays. The last array
98 : : * element is left as NULL for the convenience of outside code that wants
99 : : * to sequentially scan these arrays.
100 : : */
101 : : char *localized_abbrev_days[7 + 1];
102 : : char *localized_full_days[7 + 1];
103 : : char *localized_abbrev_months[12 + 1];
104 : : char *localized_full_months[12 + 1];
105 : :
106 : : static pg_locale_t default_locale = NULL;
107 : :
108 : : /* indicates whether locale information cache is valid */
109 : : static bool CurrentLocaleConvValid = false;
110 : : static bool CurrentLCTimeValid = false;
111 : :
112 : : static struct pg_locale_struct c_locale = {
113 : : .deterministic = true,
114 : : .collate_is_c = true,
115 : : .ctype_is_c = true,
116 : : };
117 : :
118 : : /* Cache for collation-related knowledge */
119 : :
120 : : typedef struct
121 : : {
122 : : Oid collid; /* hash key: pg_collation OID */
123 : : pg_locale_t locale; /* locale_t struct, or 0 if not valid */
124 : :
125 : : /* needed for simplehash */
126 : : uint32 hash;
127 : : char status;
128 : : } collation_cache_entry;
129 : :
130 : : #define SH_PREFIX collation_cache
131 : : #define SH_ELEMENT_TYPE collation_cache_entry
132 : : #define SH_KEY_TYPE Oid
133 : : #define SH_KEY collid
134 : : #define SH_HASH_KEY(tb, key) murmurhash32((uint32) key)
135 : : #define SH_EQUAL(tb, a, b) (a == b)
136 : : #define SH_GET_HASH(tb, a) a->hash
137 : : #define SH_SCOPE static inline
138 : : #define SH_STORE_HASH
139 : : #define SH_DECLARE
140 : : #define SH_DEFINE
141 : : #include "lib/simplehash.h"
142 : :
143 : : static MemoryContext CollationCacheContext = NULL;
144 : : static collation_cache_hash *CollationCache = NULL;
145 : :
146 : : /*
147 : : * The collation cache is often accessed repeatedly for the same collation, so
148 : : * remember the last one used.
149 : : */
150 : : static Oid last_collation_cache_oid = InvalidOid;
151 : : static pg_locale_t last_collation_cache_locale = NULL;
152 : :
153 : : #if defined(WIN32) && defined(LC_MESSAGES)
154 : : static char *IsoLocaleName(const char *);
155 : : #endif
156 : :
157 : : /*
158 : : * pg_perm_setlocale
159 : : *
160 : : * This wraps the libc function setlocale(), with two additions. First, when
161 : : * changing LC_CTYPE, update gettext's encoding for the current message
162 : : * domain. GNU gettext automatically tracks LC_CTYPE on most platforms, but
163 : : * not on Windows. Second, if the operation is successful, the corresponding
164 : : * LC_XXX environment variable is set to match. By setting the environment
165 : : * variable, we ensure that any subsequent use of setlocale(..., "") will
166 : : * preserve the settings made through this routine. Of course, LC_ALL must
167 : : * also be unset to fully ensure that, but that has to be done elsewhere after
168 : : * all the individual LC_XXX variables have been set correctly. (Thank you
169 : : * Perl for making this kluge necessary.)
170 : : */
171 : : char *
172 : 2577 : pg_perm_setlocale(int category, const char *locale)
173 : : {
174 : 2577 : char *result;
175 : 2577 : const char *envvar;
176 : :
177 : : #ifndef WIN32
178 : 2577 : result = setlocale(category, locale);
179 : : #else
180 : :
181 : : /*
182 : : * On Windows, setlocale(LC_MESSAGES) does not work, so just assume that
183 : : * the given value is good and set it in the environment variables. We
184 : : * must ignore attempts to set to "", which means "keep using the old
185 : : * environment value".
186 : : */
187 : : #ifdef LC_MESSAGES
188 : : if (category == LC_MESSAGES)
189 : : {
190 : : result = (char *) locale;
191 : : if (locale == NULL || locale[0] == '\0')
192 : : return result;
193 : : }
194 : : else
195 : : #endif
196 : : result = setlocale(category, locale);
197 : : #endif /* WIN32 */
198 : :
199 [ + - ]: 2577 : if (result == NULL)
200 : 0 : return result; /* fall out immediately on failure */
201 : :
202 : : /*
203 : : * Use the right encoding in translated messages. Under ENABLE_NLS, let
204 : : * pg_bind_textdomain_codeset() figure it out. Under !ENABLE_NLS, message
205 : : * format strings are ASCII, but database-encoding strings may enter the
206 : : * message via %s. This makes the overall message encoding equal to the
207 : : * database encoding.
208 : : */
209 [ + + ]: 2577 : if (category == LC_CTYPE)
210 : : {
211 : : static char save_lc_ctype[LOCALE_NAME_BUFLEN];
212 : :
213 : : /* copy setlocale() return value before callee invokes it again */
214 : 803 : strlcpy(save_lc_ctype, result, sizeof(save_lc_ctype));
215 : 803 : result = save_lc_ctype;
216 : :
217 : : #ifdef ENABLE_NLS
218 : 803 : SetMessageEncoding(pg_bind_textdomain_codeset(textdomain(NULL)));
219 : : #else
220 : : SetMessageEncoding(GetDatabaseEncoding());
221 : : #endif
222 : 803 : }
223 : :
224 [ + + + + : 2577 : switch (category)
+ + - ]
225 : : {
226 : : case LC_COLLATE:
227 : 8 : envvar = "LC_COLLATE";
228 : 8 : break;
229 : : case LC_CTYPE:
230 : 803 : envvar = "LC_CTYPE";
231 : 803 : break;
232 : : #ifdef LC_MESSAGES
233 : : case LC_MESSAGES:
234 : 1742 : envvar = "LC_MESSAGES";
235 : : #ifdef WIN32
236 : : result = IsoLocaleName(locale);
237 : : if (result == NULL)
238 : : result = (char *) locale;
239 : : elog(DEBUG3, "IsoLocaleName() executed; locale: \"%s\"", result);
240 : : #endif /* WIN32 */
241 : 1742 : break;
242 : : #endif /* LC_MESSAGES */
243 : : case LC_MONETARY:
244 : 8 : envvar = "LC_MONETARY";
245 : 8 : break;
246 : : case LC_NUMERIC:
247 : 8 : envvar = "LC_NUMERIC";
248 : 8 : break;
249 : : case LC_TIME:
250 : 8 : envvar = "LC_TIME";
251 : 8 : break;
252 : : default:
253 [ # # # # ]: 0 : elog(FATAL, "unrecognized LC category: %d", category);
254 : 0 : return NULL; /* keep compiler quiet */
255 : : }
256 : :
257 [ - + ]: 2577 : if (setenv(envvar, result, 1) != 0)
258 : 0 : return NULL;
259 : :
260 : 2577 : return result;
261 : 2577 : }
262 : :
263 : :
264 : : /*
265 : : * Is the locale name valid for the locale category?
266 : : *
267 : : * If successful, and canonname isn't NULL, a palloc'd copy of the locale's
268 : : * canonical name is stored there. This is especially useful for figuring out
269 : : * what locale name "" means (ie, the server environment value). (Actually,
270 : : * it seems that on most implementations that's the only thing it's good for;
271 : : * we could wish that setlocale gave back a canonically spelled version of
272 : : * the locale name, but typically it doesn't.)
273 : : */
274 : : bool
275 : 6466 : check_locale(int category, const char *locale, char **canonname)
276 : : {
277 : 6466 : char *save;
278 : 6466 : char *res;
279 : :
280 : : /* Don't let Windows' non-ASCII locale names in. */
281 [ + - ]: 6466 : if (!pg_is_ascii(locale))
282 : : {
283 [ # # # # ]: 0 : ereport(WARNING,
284 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285 : : errmsg("locale name \"%s\" contains non-ASCII characters",
286 : : locale)));
287 : 0 : return false;
288 : : }
289 : :
290 [ + + ]: 6466 : if (canonname)
291 : 10 : *canonname = NULL; /* in case of failure */
292 : :
293 : 6466 : save = setlocale(category, NULL);
294 [ + - ]: 6466 : if (!save)
295 : 0 : return false; /* won't happen, we hope */
296 : :
297 : : /* save may be pointing at a modifiable scratch variable, see above. */
298 : 6466 : save = pstrdup(save);
299 : :
300 : : /* set the locale with setlocale, to see if it accepts it. */
301 : 6466 : res = setlocale(category, locale);
302 : :
303 : : /* save canonical name if requested. */
304 [ + - + + ]: 6466 : if (res && canonname)
305 : 10 : *canonname = pstrdup(res);
306 : :
307 : : /* restore old value. */
308 [ + - ]: 6466 : if (!setlocale(category, save))
309 [ # # # # ]: 0 : elog(WARNING, "failed to restore old locale \"%s\"", save);
310 : 6466 : pfree(save);
311 : :
312 : : /* Don't let Windows' non-ASCII locale names out. */
313 [ + + + - : 6466 : if (canonname && *canonname && !pg_is_ascii(*canonname))
+ - ]
314 : : {
315 [ # # # # ]: 0 : ereport(WARNING,
316 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
317 : : errmsg("locale name \"%s\" contains non-ASCII characters",
318 : : *canonname)));
319 : 0 : pfree(*canonname);
320 : 0 : *canonname = NULL;
321 : 0 : return false;
322 : : }
323 : :
324 : 6466 : return (res != NULL);
325 : 6466 : }
326 : :
327 : :
328 : : /*
329 : : * GUC check/assign hooks
330 : : *
331 : : * For most locale categories, the assign hook doesn't actually set the locale
332 : : * permanently, just reset flags so that the next use will cache the
333 : : * appropriate values. (See explanation at the top of this file.)
334 : : *
335 : : * Note: we accept value = "" as selecting the postmaster's environment
336 : : * value, whatever it was (so long as the environment setting is legal).
337 : : * This will have been locked down by an earlier call to pg_perm_setlocale.
338 : : */
339 : : bool
340 : 1734 : check_locale_monetary(char **newval, void **extra, GucSource source)
341 : : {
342 : 1734 : return check_locale(LC_MONETARY, *newval, NULL);
343 : : }
344 : :
345 : : void
346 : 1731 : assign_locale_monetary(const char *newval, void *extra)
347 : : {
348 : 1731 : CurrentLocaleConvValid = false;
349 : 1731 : }
350 : :
351 : : bool
352 : 1735 : check_locale_numeric(char **newval, void **extra, GucSource source)
353 : : {
354 : 1735 : return check_locale(LC_NUMERIC, *newval, NULL);
355 : : }
356 : :
357 : : void
358 : 1733 : assign_locale_numeric(const char *newval, void *extra)
359 : : {
360 : 1733 : CurrentLocaleConvValid = false;
361 : 1733 : }
362 : :
363 : : bool
364 : 1734 : check_locale_time(char **newval, void **extra, GucSource source)
365 : : {
366 : 1734 : return check_locale(LC_TIME, *newval, NULL);
367 : : }
368 : :
369 : : void
370 : 1731 : assign_locale_time(const char *newval, void *extra)
371 : : {
372 : 1731 : CurrentLCTimeValid = false;
373 : 1731 : }
374 : :
375 : : /*
376 : : * We allow LC_MESSAGES to actually be set globally.
377 : : *
378 : : * Note: we normally disallow value = "" because it wouldn't have consistent
379 : : * semantics (it'd effectively just use the previous value). However, this
380 : : * is the value passed for PGC_S_DEFAULT, so don't complain in that case,
381 : : * not even if the attempted setting fails due to invalid environment value.
382 : : * The idea there is just to accept the environment setting *if possible*
383 : : * during startup, until we can read the proper value from postgresql.conf.
384 : : */
385 : : bool
386 : 1736 : check_locale_messages(char **newval, void **extra, GucSource source)
387 : : {
388 [ + + ]: 1736 : if (**newval == '\0')
389 : : {
390 [ - + ]: 483 : if (source == PGC_S_DEFAULT)
391 : 483 : return true;
392 : : else
393 : 0 : return false;
394 : : }
395 : :
396 : : /*
397 : : * LC_MESSAGES category does not exist everywhere, but accept it anyway
398 : : *
399 : : * On Windows, we can't even check the value, so accept blindly
400 : : */
401 : : #if defined(LC_MESSAGES) && !defined(WIN32)
402 : 1253 : return check_locale(LC_MESSAGES, *newval, NULL);
403 : : #else
404 : : return true;
405 : : #endif
406 : 1736 : }
407 : :
408 : : void
409 : 1734 : assign_locale_messages(const char *newval, void *extra)
410 : : {
411 : : /*
412 : : * LC_MESSAGES category does not exist everywhere, but accept it anyway.
413 : : * We ignore failure, as per comment above.
414 : : */
415 : : #ifdef LC_MESSAGES
416 : 1734 : (void) pg_perm_setlocale(LC_MESSAGES, newval);
417 : : #endif
418 : 1734 : }
419 : :
420 : :
421 : : /*
422 : : * Frees the malloced content of a struct lconv. (But not the struct
423 : : * itself.) It's important that this not throw elog(ERROR).
424 : : */
425 : : static void
426 : 1 : free_struct_lconv(struct lconv *s)
427 : : {
428 : 1 : free(s->decimal_point);
429 : 1 : free(s->thousands_sep);
430 : 1 : free(s->grouping);
431 : 1 : free(s->int_curr_symbol);
432 : 1 : free(s->currency_symbol);
433 : 1 : free(s->mon_decimal_point);
434 : 1 : free(s->mon_thousands_sep);
435 : 1 : free(s->mon_grouping);
436 : 1 : free(s->positive_sign);
437 : 1 : free(s->negative_sign);
438 : 1 : }
439 : :
440 : : /*
441 : : * Check that all fields of a struct lconv (or at least, the ones we care
442 : : * about) are non-NULL. The field list must match free_struct_lconv().
443 : : */
444 : : static bool
445 : 7 : struct_lconv_is_valid(struct lconv *s)
446 : : {
447 [ + - ]: 7 : if (s->decimal_point == NULL)
448 : 0 : return false;
449 [ + - ]: 7 : if (s->thousands_sep == NULL)
450 : 0 : return false;
451 [ + - ]: 7 : if (s->grouping == NULL)
452 : 0 : return false;
453 [ + - ]: 7 : if (s->int_curr_symbol == NULL)
454 : 0 : return false;
455 [ + - ]: 7 : if (s->currency_symbol == NULL)
456 : 0 : return false;
457 [ + - ]: 7 : if (s->mon_decimal_point == NULL)
458 : 0 : return false;
459 [ + - ]: 7 : if (s->mon_thousands_sep == NULL)
460 : 0 : return false;
461 [ + - ]: 7 : if (s->mon_grouping == NULL)
462 : 0 : return false;
463 [ + - ]: 7 : if (s->positive_sign == NULL)
464 : 0 : return false;
465 [ + - ]: 7 : if (s->negative_sign == NULL)
466 : 0 : return false;
467 : 7 : return true;
468 : 7 : }
469 : :
470 : :
471 : : /*
472 : : * Convert the strdup'd string at *str from the specified encoding to the
473 : : * database encoding.
474 : : */
475 : : static void
476 : 56 : db_encoding_convert(int encoding, char **str)
477 : : {
478 : 56 : char *pstr;
479 : 56 : char *mstr;
480 : :
481 : : /* convert the string to the database encoding */
482 : 56 : pstr = pg_any_to_server(*str, strlen(*str), encoding);
483 [ - + ]: 56 : if (pstr == *str)
484 : 56 : return; /* no conversion happened */
485 : :
486 : : /* need it malloc'd not palloc'd */
487 : 0 : mstr = strdup(pstr);
488 [ # # ]: 0 : if (mstr == NULL)
489 [ # # # # ]: 0 : ereport(ERROR,
490 : : (errcode(ERRCODE_OUT_OF_MEMORY),
491 : : errmsg("out of memory")));
492 : :
493 : : /* replace old string */
494 : 0 : free(*str);
495 : 0 : *str = mstr;
496 : :
497 : 0 : pfree(pstr);
498 [ - + ]: 56 : }
499 : :
500 : :
501 : : /*
502 : : * Return the POSIX lconv struct (contains number/money formatting
503 : : * information) with locale information for all categories.
504 : : */
505 : : struct lconv *
506 : 287 : PGLC_localeconv(void)
507 : : {
508 : : static struct lconv CurrentLocaleConv;
509 : : static bool CurrentLocaleConvAllocated = false;
510 : 287 : struct lconv *extlconv;
511 : 287 : struct lconv tmp;
512 : 287 : struct lconv worklconv = {0};
513 : :
514 : : /* Did we do it already? */
515 [ + + ]: 287 : if (CurrentLocaleConvValid)
516 : 280 : return &CurrentLocaleConv;
517 : :
518 : : /* Free any already-allocated storage */
519 [ + + ]: 7 : if (CurrentLocaleConvAllocated)
520 : : {
521 : 1 : free_struct_lconv(&CurrentLocaleConv);
522 : 1 : CurrentLocaleConvAllocated = false;
523 : 1 : }
524 : :
525 : : /*
526 : : * Use thread-safe method of obtaining a copy of lconv from the operating
527 : : * system.
528 : : */
529 : 14 : if (pg_localeconv_r(locale_monetary,
530 : 7 : locale_numeric,
531 [ + - ]: 7 : &tmp) != 0)
532 [ # # # # ]: 0 : elog(ERROR,
533 : : "could not get lconv for LC_MONETARY = \"%s\", LC_NUMERIC = \"%s\": %m",
534 : : locale_monetary, locale_numeric);
535 : :
536 : : /* Must copy data now so we can re-encode it. */
537 : 7 : extlconv = &tmp;
538 : 7 : worklconv.decimal_point = strdup(extlconv->decimal_point);
539 : 7 : worklconv.thousands_sep = strdup(extlconv->thousands_sep);
540 : 7 : worklconv.grouping = strdup(extlconv->grouping);
541 : 7 : worklconv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
542 : 7 : worklconv.currency_symbol = strdup(extlconv->currency_symbol);
543 : 7 : worklconv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
544 : 7 : worklconv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
545 : 7 : worklconv.mon_grouping = strdup(extlconv->mon_grouping);
546 : 7 : worklconv.positive_sign = strdup(extlconv->positive_sign);
547 : 7 : worklconv.negative_sign = strdup(extlconv->negative_sign);
548 : : /* Copy scalar fields as well */
549 : 7 : worklconv.int_frac_digits = extlconv->int_frac_digits;
550 : 7 : worklconv.frac_digits = extlconv->frac_digits;
551 : 7 : worklconv.p_cs_precedes = extlconv->p_cs_precedes;
552 : 7 : worklconv.p_sep_by_space = extlconv->p_sep_by_space;
553 : 7 : worklconv.n_cs_precedes = extlconv->n_cs_precedes;
554 : 7 : worklconv.n_sep_by_space = extlconv->n_sep_by_space;
555 : 7 : worklconv.p_sign_posn = extlconv->p_sign_posn;
556 : 7 : worklconv.n_sign_posn = extlconv->n_sign_posn;
557 : :
558 : : /* Free the contents of the object populated by pg_localeconv_r(). */
559 : 7 : pg_localeconv_free(&tmp);
560 : :
561 : : /* If any of the preceding strdup calls failed, complain now. */
562 [ + - ]: 7 : if (!struct_lconv_is_valid(&worklconv))
563 [ # # # # ]: 0 : ereport(ERROR,
564 : : (errcode(ERRCODE_OUT_OF_MEMORY),
565 : : errmsg("out of memory")));
566 : :
567 [ + - ]: 7 : PG_TRY();
568 : : {
569 : 7 : int encoding;
570 : :
571 : : /*
572 : : * Now we must perform encoding conversion from whatever's associated
573 : : * with the locales into the database encoding. If we can't identify
574 : : * the encoding implied by LC_NUMERIC or LC_MONETARY (ie we get -1),
575 : : * use PG_SQL_ASCII, which will result in just validating that the
576 : : * strings are OK in the database encoding.
577 : : */
578 : 7 : encoding = pg_get_encoding_from_locale(locale_numeric, true);
579 [ + - ]: 7 : if (encoding < 0)
580 : 0 : encoding = PG_SQL_ASCII;
581 : :
582 : 7 : db_encoding_convert(encoding, &worklconv.decimal_point);
583 : 7 : db_encoding_convert(encoding, &worklconv.thousands_sep);
584 : : /* grouping is not text and does not require conversion */
585 : :
586 : 7 : encoding = pg_get_encoding_from_locale(locale_monetary, true);
587 [ + - ]: 7 : if (encoding < 0)
588 : 0 : encoding = PG_SQL_ASCII;
589 : :
590 : 7 : db_encoding_convert(encoding, &worklconv.int_curr_symbol);
591 : 7 : db_encoding_convert(encoding, &worklconv.currency_symbol);
592 : 7 : db_encoding_convert(encoding, &worklconv.mon_decimal_point);
593 : 7 : db_encoding_convert(encoding, &worklconv.mon_thousands_sep);
594 : : /* mon_grouping is not text and does not require conversion */
595 : 7 : db_encoding_convert(encoding, &worklconv.positive_sign);
596 : 7 : db_encoding_convert(encoding, &worklconv.negative_sign);
597 : 7 : }
598 : 7 : PG_CATCH();
599 : : {
600 : 0 : free_struct_lconv(&worklconv);
601 : 0 : PG_RE_THROW();
602 : : }
603 [ + - ]: 7 : PG_END_TRY();
604 : :
605 : : /*
606 : : * Everything is good, so save the results.
607 : : */
608 : 7 : CurrentLocaleConv = worklconv;
609 : 7 : CurrentLocaleConvAllocated = true;
610 : 7 : CurrentLocaleConvValid = true;
611 : 7 : return &CurrentLocaleConv;
612 : 287 : }
613 : :
614 : : #ifdef WIN32
615 : : /*
616 : : * On Windows, strftime() returns its output in encoding CP_ACP (the default
617 : : * operating system codepage for the computer), which is likely different
618 : : * from SERVER_ENCODING. This is especially important in Japanese versions
619 : : * of Windows which will use SJIS encoding, which we don't support as a
620 : : * server encoding.
621 : : *
622 : : * So, instead of using strftime(), use wcsftime() to return the value in
623 : : * wide characters (internally UTF16) and then convert to UTF8, which we
624 : : * know how to handle directly.
625 : : *
626 : : * Note that this only affects the calls to strftime() in this file, which are
627 : : * used to get the locale-aware strings. Other parts of the backend use
628 : : * pg_strftime(), which isn't locale-aware and does not need to be replaced.
629 : : */
630 : : static size_t
631 : : strftime_l_win32(char *dst, size_t dstlen,
632 : : const char *format, const struct tm *tm, locale_t locale)
633 : : {
634 : : size_t len;
635 : : wchar_t wformat[8]; /* formats used below need 3 chars */
636 : : wchar_t wbuf[MAX_L10N_DATA];
637 : :
638 : : /*
639 : : * Get a wchar_t version of the format string. We only actually use
640 : : * plain-ASCII formats in this file, so we can say that they're UTF8.
641 : : */
642 : : len = MultiByteToWideChar(CP_UTF8, 0, format, -1,
643 : : wformat, lengthof(wformat));
644 : : if (len == 0)
645 : : elog(ERROR, "could not convert format string from UTF-8: error code %lu",
646 : : GetLastError());
647 : :
648 : : len = _wcsftime_l(wbuf, MAX_L10N_DATA, wformat, tm, locale);
649 : : if (len == 0)
650 : : {
651 : : /*
652 : : * wcsftime failed, possibly because the result would not fit in
653 : : * MAX_L10N_DATA. Return 0 with the contents of dst unspecified.
654 : : */
655 : : return 0;
656 : : }
657 : :
658 : : len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dst, dstlen - 1,
659 : : NULL, NULL);
660 : : if (len == 0)
661 : : elog(ERROR, "could not convert string to UTF-8: error code %lu",
662 : : GetLastError());
663 : :
664 : : dst[len] = '\0';
665 : :
666 : : return len;
667 : : }
668 : :
669 : : /* redefine strftime_l() */
670 : : #define strftime_l(a,b,c,d,e) strftime_l_win32(a,b,c,d,e)
671 : : #endif /* WIN32 */
672 : :
673 : : /*
674 : : * Subroutine for cache_locale_time().
675 : : * Convert the given string from encoding "encoding" to the database
676 : : * encoding, and store the result at *dst, replacing any previous value.
677 : : */
678 : : static void
679 : 266 : cache_single_string(char **dst, const char *src, int encoding)
680 : : {
681 : 266 : char *ptr;
682 : 266 : char *olddst;
683 : :
684 : : /* Convert the string to the database encoding, or validate it's OK */
685 : 266 : ptr = pg_any_to_server(src, strlen(src), encoding);
686 : :
687 : : /* Store the string in long-lived storage, replacing any previous value */
688 : 266 : olddst = *dst;
689 : 266 : *dst = MemoryContextStrdup(TopMemoryContext, ptr);
690 [ + - ]: 266 : if (olddst)
691 : 0 : pfree(olddst);
692 : :
693 : : /* Might as well clean up any palloc'd conversion result, too */
694 [ + - ]: 266 : if (ptr != src)
695 : 0 : pfree(ptr);
696 : 266 : }
697 : :
698 : : /*
699 : : * Update the lc_time localization cache variables if needed.
700 : : */
701 : : void
702 : 8252 : cache_locale_time(void)
703 : : {
704 : 8252 : char buf[(2 * 7 + 2 * 12) * MAX_L10N_DATA];
705 : 8252 : char *bufptr;
706 : 8252 : time_t timenow;
707 : 8252 : struct tm *timeinfo;
708 : 8252 : struct tm timeinfobuf;
709 : 8252 : bool strftimefail = false;
710 : 8252 : int encoding;
711 : 8252 : int i;
712 : 8252 : locale_t locale;
713 : :
714 : : /* did we do this already? */
715 [ + + ]: 8252 : if (CurrentLCTimeValid)
716 : 8245 : return;
717 : :
718 [ - + - + ]: 7 : elog(DEBUG3, "cache_locale_time() executed; locale: \"%s\"", locale_time);
719 : :
720 : 7 : errno = ENOENT;
721 : : #ifdef WIN32
722 : : locale = _create_locale(LC_ALL, locale_time);
723 : : if (locale == (locale_t) 0)
724 : : _dosmaperr(GetLastError());
725 : : #else
726 : 7 : locale = newlocale(LC_ALL_MASK, locale_time, (locale_t) 0);
727 : : #endif
728 [ + - ]: 7 : if (!locale)
729 : 0 : report_newlocale_failure(locale_time);
730 : :
731 : : /* We use times close to current time as data for strftime(). */
732 : 7 : timenow = time(NULL);
733 : 7 : timeinfo = gmtime_r(&timenow, &timeinfobuf);
734 : :
735 : : /* Store the strftime results in MAX_L10N_DATA-sized portions of buf[] */
736 : 7 : bufptr = buf;
737 : :
738 : : /*
739 : : * MAX_L10N_DATA is sufficient buffer space for every known locale, and
740 : : * POSIX defines no strftime() errors. (Buffer space exhaustion is not an
741 : : * error.) An implementation might report errors (e.g. ENOMEM) by
742 : : * returning 0 (or, less plausibly, a negative value) and setting errno.
743 : : * Report errno just in case the implementation did that, but clear it in
744 : : * advance of the calls so we don't emit a stale, unrelated errno.
745 : : */
746 : 7 : errno = 0;
747 : :
748 : : /* localized days */
749 [ + + ]: 56 : for (i = 0; i < 7; i++)
750 : : {
751 : 49 : timeinfo->tm_wday = i;
752 [ + - ]: 49 : if (strftime_l(bufptr, MAX_L10N_DATA, "%a", timeinfo, locale) <= 0)
753 : 0 : strftimefail = true;
754 : 49 : bufptr += MAX_L10N_DATA;
755 [ + - ]: 49 : if (strftime_l(bufptr, MAX_L10N_DATA, "%A", timeinfo, locale) <= 0)
756 : 0 : strftimefail = true;
757 : 49 : bufptr += MAX_L10N_DATA;
758 : 49 : }
759 : :
760 : : /* localized months */
761 [ + + ]: 91 : for (i = 0; i < 12; i++)
762 : : {
763 : 84 : timeinfo->tm_mon = i;
764 : 84 : timeinfo->tm_mday = 1; /* make sure we don't have invalid date */
765 [ + - ]: 84 : if (strftime_l(bufptr, MAX_L10N_DATA, "%b", timeinfo, locale) <= 0)
766 : 0 : strftimefail = true;
767 : 84 : bufptr += MAX_L10N_DATA;
768 [ + - ]: 84 : if (strftime_l(bufptr, MAX_L10N_DATA, "%B", timeinfo, locale) <= 0)
769 : 0 : strftimefail = true;
770 : 84 : bufptr += MAX_L10N_DATA;
771 : 84 : }
772 : :
773 : : #ifdef WIN32
774 : : _free_locale(locale);
775 : : #else
776 : 7 : freelocale(locale);
777 : : #endif
778 : :
779 : : /*
780 : : * At this point we've done our best to clean up, and can throw errors, or
781 : : * call functions that might throw errors, with a clean conscience.
782 : : */
783 [ + - ]: 7 : if (strftimefail)
784 [ # # # # ]: 0 : elog(ERROR, "strftime_l() failed");
785 : :
786 : : #ifndef WIN32
787 : :
788 : : /*
789 : : * As in PGLC_localeconv(), we must convert strftime()'s output from the
790 : : * encoding implied by LC_TIME to the database encoding. If we can't
791 : : * identify the LC_TIME encoding, just perform encoding validation.
792 : : */
793 : 7 : encoding = pg_get_encoding_from_locale(locale_time, true);
794 [ + - ]: 7 : if (encoding < 0)
795 : 0 : encoding = PG_SQL_ASCII;
796 : :
797 : : #else
798 : :
799 : : /*
800 : : * On Windows, strftime_win32() always returns UTF8 data, so convert from
801 : : * that if necessary.
802 : : */
803 : : encoding = PG_UTF8;
804 : :
805 : : #endif /* WIN32 */
806 : :
807 : 7 : bufptr = buf;
808 : :
809 : : /* localized days */
810 [ + + ]: 56 : for (i = 0; i < 7; i++)
811 : : {
812 : 49 : cache_single_string(&localized_abbrev_days[i], bufptr, encoding);
813 : 49 : bufptr += MAX_L10N_DATA;
814 : 49 : cache_single_string(&localized_full_days[i], bufptr, encoding);
815 : 49 : bufptr += MAX_L10N_DATA;
816 : 49 : }
817 : 7 : localized_abbrev_days[7] = NULL;
818 : 7 : localized_full_days[7] = NULL;
819 : :
820 : : /* localized months */
821 [ + + ]: 91 : for (i = 0; i < 12; i++)
822 : : {
823 : 84 : cache_single_string(&localized_abbrev_months[i], bufptr, encoding);
824 : 84 : bufptr += MAX_L10N_DATA;
825 : 84 : cache_single_string(&localized_full_months[i], bufptr, encoding);
826 : 84 : bufptr += MAX_L10N_DATA;
827 : 84 : }
828 : 7 : localized_abbrev_months[12] = NULL;
829 : 7 : localized_full_months[12] = NULL;
830 : :
831 : 7 : CurrentLCTimeValid = true;
832 [ - + ]: 8252 : }
833 : :
834 : :
835 : : #if defined(WIN32) && defined(LC_MESSAGES)
836 : : /*
837 : : * Convert a Windows setlocale() argument to a Unix-style one.
838 : : *
839 : : * Regardless of platform, we install message catalogs under a Unix-style
840 : : * LL[_CC][.ENCODING][@VARIANT] naming convention. Only LC_MESSAGES settings
841 : : * following that style will elicit localized interface strings.
842 : : *
843 : : * Before Visual Studio 2012 (msvcr110.dll), Windows setlocale() accepted "C"
844 : : * (but not "c") and strings of the form <Language>[_<Country>][.<CodePage>],
845 : : * case-insensitive. setlocale() returns the fully-qualified form; for
846 : : * example, setlocale("thaI") returns "Thai_Thailand.874". Internally,
847 : : * setlocale() and _create_locale() select a "locale identifier"[1] and store
848 : : * it in an undocumented _locale_t field. From that LCID, we can retrieve the
849 : : * ISO 639 language and the ISO 3166 country. Character encoding does not
850 : : * matter, because the server and client encodings govern that.
851 : : *
852 : : * Windows Vista introduced the "locale name" concept[2], closely following
853 : : * RFC 4646. Locale identifiers are now deprecated. Starting with Visual
854 : : * Studio 2012, setlocale() accepts locale names in addition to the strings it
855 : : * accepted historically. It does not standardize them; setlocale("Th-tH")
856 : : * returns "Th-tH". setlocale(category, "") still returns a traditional
857 : : * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
858 : : * content to carry locale names instead of locale identifiers.
859 : : *
860 : : * Visual Studio 2015 should still be able to do the same as Visual Studio
861 : : * 2012, but the declaration of locale_name is missing in _locale_t, causing
862 : : * this code compilation to fail, hence this falls back instead on to
863 : : * enumerating all system locales by using EnumSystemLocalesEx to find the
864 : : * required locale name. If the input argument is in Unix-style then we can
865 : : * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
866 : : * LOCALE_SNAME.
867 : : *
868 : : * This function returns a pointer to a static buffer bearing the converted
869 : : * name or NULL if conversion fails.
870 : : *
871 : : * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
872 : : * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
873 : : */
874 : :
875 : : /*
876 : : * Callback function for EnumSystemLocalesEx() in get_iso_localename().
877 : : *
878 : : * This function enumerates all system locales, searching for one that matches
879 : : * an input with the format: <Language>[_<Country>], e.g.
880 : : * English[_United States]
881 : : *
882 : : * The input is a three wchar_t array as an LPARAM. The first element is the
883 : : * locale_name we want to match, the second element is an allocated buffer
884 : : * where the Unix-style locale is copied if a match is found, and the third
885 : : * element is the search status, 1 if a match was found, 0 otherwise.
886 : : */
887 : : static BOOL CALLBACK
888 : : search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
889 : : {
890 : : wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
891 : : wchar_t **argv;
892 : :
893 : : (void) (dwFlags);
894 : :
895 : : argv = (wchar_t **) lparam;
896 : : *argv[2] = (wchar_t) 0;
897 : :
898 : : memset(test_locale, 0, sizeof(test_locale));
899 : :
900 : : /* Get the name of the <Language> in English */
901 : : if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
902 : : test_locale, LOCALE_NAME_MAX_LENGTH))
903 : : {
904 : : /*
905 : : * If the enumerated locale does not have a hyphen ("en") OR the
906 : : * locale_name input does not have an underscore ("English"), we only
907 : : * need to compare the <Language> tags.
908 : : */
909 : : if (wcsrchr(pStr, '-') == NULL || wcsrchr(argv[0], '_') == NULL)
910 : : {
911 : : if (_wcsicmp(argv[0], test_locale) == 0)
912 : : {
913 : : wcscpy(argv[1], pStr);
914 : : *argv[2] = (wchar_t) 1;
915 : : return FALSE;
916 : : }
917 : : }
918 : :
919 : : /*
920 : : * We have to compare a full <Language>_<Country> tag, so we append
921 : : * the underscore and name of the country/region in English, e.g.
922 : : * "English_United States".
923 : : */
924 : : else
925 : : {
926 : : size_t len;
927 : :
928 : : wcscat(test_locale, L"_");
929 : : len = wcslen(test_locale);
930 : : if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
931 : : test_locale + len,
932 : : LOCALE_NAME_MAX_LENGTH - len))
933 : : {
934 : : if (_wcsicmp(argv[0], test_locale) == 0)
935 : : {
936 : : wcscpy(argv[1], pStr);
937 : : *argv[2] = (wchar_t) 1;
938 : : return FALSE;
939 : : }
940 : : }
941 : : }
942 : : }
943 : :
944 : : return TRUE;
945 : : }
946 : :
947 : : /*
948 : : * This function converts a Windows locale name to an ISO formatted version
949 : : * for Visual Studio 2015 or greater.
950 : : *
951 : : * Returns NULL, if no valid conversion was found.
952 : : */
953 : : static char *
954 : : get_iso_localename(const char *winlocname)
955 : : {
956 : : wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
957 : : wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
958 : : static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
959 : : const char *period;
960 : : int len;
961 : : int ret_val;
962 : :
963 : : /*
964 : : * Valid locales have the following syntax:
965 : : * <Language>[_<Country>[.<CodePage>]]
966 : : *
967 : : * GetLocaleInfoEx can only take locale name without code-page and for the
968 : : * purpose of this API the code-page doesn't matter.
969 : : */
970 : : period = strchr(winlocname, '.');
971 : : if (period != NULL)
972 : : len = period - winlocname;
973 : : else
974 : : len = pg_mbstrlen(winlocname);
975 : :
976 : : memset(wc_locale_name, 0, sizeof(wc_locale_name));
977 : : memset(buffer, 0, sizeof(buffer));
978 : : MultiByteToWideChar(CP_ACP, 0, winlocname, len, wc_locale_name,
979 : : LOCALE_NAME_MAX_LENGTH);
980 : :
981 : : /*
982 : : * If the lc_messages is already a Unix-style string, we have a direct
983 : : * match with LOCALE_SNAME, e.g. en-US, en_US.
984 : : */
985 : : ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
986 : : LOCALE_NAME_MAX_LENGTH);
987 : : if (!ret_val)
988 : : {
989 : : /*
990 : : * Search for a locale in the system that matches language and country
991 : : * name.
992 : : */
993 : : wchar_t *argv[3];
994 : :
995 : : argv[0] = wc_locale_name;
996 : : argv[1] = buffer;
997 : : argv[2] = (wchar_t *) &ret_val;
998 : : EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
999 : : NULL);
1000 : : }
1001 : :
1002 : : if (ret_val)
1003 : : {
1004 : : size_t rc;
1005 : : char *hyphen;
1006 : :
1007 : : /* Locale names use only ASCII, any conversion locale suffices. */
1008 : : rc = wchar2char(iso_lc_messages, buffer, sizeof(iso_lc_messages), NULL);
1009 : : if (rc == -1 || rc == sizeof(iso_lc_messages))
1010 : : return NULL;
1011 : :
1012 : : /*
1013 : : * Since the message catalogs sit on a case-insensitive filesystem, we
1014 : : * need not standardize letter case here. So long as we do not ship
1015 : : * message catalogs for which it would matter, we also need not
1016 : : * translate the script/variant portion, e.g. uz-Cyrl-UZ to
1017 : : * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
1018 : : */
1019 : : hyphen = strchr(iso_lc_messages, '-');
1020 : : if (hyphen)
1021 : : *hyphen = '_';
1022 : : return iso_lc_messages;
1023 : : }
1024 : :
1025 : : return NULL;
1026 : : }
1027 : :
1028 : : static char *
1029 : : IsoLocaleName(const char *winlocname)
1030 : : {
1031 : : static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1032 : :
1033 : : if (pg_strcasecmp("c", winlocname) == 0 ||
1034 : : pg_strcasecmp("posix", winlocname) == 0)
1035 : : {
1036 : : strcpy(iso_lc_messages, "C");
1037 : : return iso_lc_messages;
1038 : : }
1039 : : else
1040 : : return get_iso_localename(winlocname);
1041 : : }
1042 : :
1043 : : #endif /* WIN32 && LC_MESSAGES */
1044 : :
1045 : : /*
1046 : : * Create a new pg_locale_t struct for the given collation oid.
1047 : : */
1048 : : static pg_locale_t
1049 : 47 : create_pg_locale(Oid collid, MemoryContext context)
1050 : : {
1051 : 47 : HeapTuple tp;
1052 : 47 : Form_pg_collation collform;
1053 : 47 : pg_locale_t result;
1054 : 47 : Datum datum;
1055 : 47 : bool isnull;
1056 : :
1057 : 47 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
1058 [ + - ]: 47 : if (!HeapTupleIsValid(tp))
1059 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
1060 : 47 : collform = (Form_pg_collation) GETSTRUCT(tp);
1061 : :
1062 [ + + ]: 47 : if (collform->collprovider == COLLPROVIDER_BUILTIN)
1063 : 8 : result = create_pg_locale_builtin(collid, context);
1064 [ + + ]: 39 : else if (collform->collprovider == COLLPROVIDER_ICU)
1065 : 28 : result = create_pg_locale_icu(collid, context);
1066 [ + - ]: 11 : else if (collform->collprovider == COLLPROVIDER_LIBC)
1067 : 11 : result = create_pg_locale_libc(collid, context);
1068 : : else
1069 : : /* shouldn't happen */
1070 [ # # # # ]: 0 : PGLOCALE_SUPPORT_ERROR(collform->collprovider);
1071 : :
1072 : 47 : result->is_default = false;
1073 : :
1074 [ + + + + : 47 : Assert((result->collate_is_c && result->collate == NULL) ||
+ - ]
1075 : : (!result->collate_is_c && result->collate != NULL));
1076 : :
1077 [ + + + + : 45 : Assert((result->ctype_is_c && result->ctype == NULL) ||
+ - ]
1078 : : (!result->ctype_is_c && result->ctype != NULL));
1079 : :
1080 : 45 : datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collversion,
1081 : : &isnull);
1082 [ + + ]: 45 : if (!isnull)
1083 : : {
1084 : 35 : char *actual_versionstr;
1085 : 35 : char *collversionstr;
1086 : :
1087 : 35 : collversionstr = TextDatumGetCString(datum);
1088 : :
1089 [ - + ]: 35 : if (collform->collprovider == COLLPROVIDER_LIBC)
1090 : 0 : datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
1091 : : else
1092 : 35 : datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_colllocale);
1093 : :
1094 : 70 : actual_versionstr = get_collation_actual_version(collform->collprovider,
1095 : 35 : TextDatumGetCString(datum));
1096 [ + - ]: 35 : if (!actual_versionstr)
1097 : : {
1098 : : /*
1099 : : * This could happen when specifying a version in CREATE COLLATION
1100 : : * but the provider does not support versioning, or manually
1101 : : * creating a mess in the catalogs.
1102 : : */
1103 [ # # # # ]: 0 : ereport(ERROR,
1104 : : (errmsg("collation \"%s\" has no actual version, but a version was recorded",
1105 : : NameStr(collform->collname))));
1106 : 0 : }
1107 : :
1108 [ + - ]: 35 : if (strcmp(actual_versionstr, collversionstr) != 0)
1109 [ # # # # ]: 0 : ereport(WARNING,
1110 : : (errmsg("collation \"%s\" has version mismatch",
1111 : : NameStr(collform->collname)),
1112 : : errdetail("The collation in the database was created using version %s, "
1113 : : "but the operating system provides version %s.",
1114 : : collversionstr, actual_versionstr),
1115 : : errhint("Rebuild all objects affected by this collation and run "
1116 : : "ALTER COLLATION %s REFRESH VERSION, "
1117 : : "or build PostgreSQL with the right library version.",
1118 : : quote_qualified_identifier(get_namespace_name(collform->collnamespace),
1119 : : NameStr(collform->collname)))));
1120 : 35 : }
1121 : :
1122 : 45 : ReleaseSysCache(tp);
1123 : :
1124 : 90 : return result;
1125 : 45 : }
1126 : :
1127 : : /*
1128 : : * Initialize default_locale with database locale settings.
1129 : : */
1130 : : void
1131 : 795 : init_database_collation(void)
1132 : : {
1133 : 795 : HeapTuple tup;
1134 : 795 : Form_pg_database dbform;
1135 : 795 : pg_locale_t result;
1136 : :
1137 [ + - ]: 795 : Assert(default_locale == NULL);
1138 : :
1139 : : /* Fetch our pg_database row normally, via syscache */
1140 : 795 : tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
1141 [ + - ]: 795 : if (!HeapTupleIsValid(tup))
1142 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
1143 : 795 : dbform = (Form_pg_database) GETSTRUCT(tup);
1144 : :
1145 [ - + ]: 795 : if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
1146 : 0 : result = create_pg_locale_builtin(DEFAULT_COLLATION_OID,
1147 : 0 : TopMemoryContext);
1148 [ - + ]: 795 : else if (dbform->datlocprovider == COLLPROVIDER_ICU)
1149 : 0 : result = create_pg_locale_icu(DEFAULT_COLLATION_OID,
1150 : 0 : TopMemoryContext);
1151 [ + - ]: 795 : else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
1152 : 795 : result = create_pg_locale_libc(DEFAULT_COLLATION_OID,
1153 : 795 : TopMemoryContext);
1154 : : else
1155 : : /* shouldn't happen */
1156 [ # # # # ]: 0 : PGLOCALE_SUPPORT_ERROR(dbform->datlocprovider);
1157 : :
1158 : 795 : result->is_default = true;
1159 : :
1160 [ - + # # : 795 : Assert((result->collate_is_c && result->collate == NULL) ||
+ - ]
1161 : : (!result->collate_is_c && result->collate != NULL));
1162 : :
1163 [ - + # # : 795 : Assert((result->ctype_is_c && result->ctype == NULL) ||
+ - ]
1164 : : (!result->ctype_is_c && result->ctype != NULL));
1165 : :
1166 : 795 : ReleaseSysCache(tup);
1167 : :
1168 : 795 : default_locale = result;
1169 : 795 : }
1170 : :
1171 : : /*
1172 : : * Get database default locale.
1173 : : */
1174 : : pg_locale_t
1175 : 49719 : pg_database_locale(void)
1176 : : {
1177 : 49719 : return pg_newlocale_from_collation(DEFAULT_COLLATION_OID);
1178 : : }
1179 : :
1180 : : /*
1181 : : * Create a pg_locale_t from a collation OID. Results are cached for the
1182 : : * lifetime of the backend. Thus, do not free the result with freelocale().
1183 : : *
1184 : : * For simplicity, we always generate COLLATE + CTYPE even though we
1185 : : * might only need one of them. Since this is called only once per session,
1186 : : * it shouldn't cost much.
1187 : : */
1188 : : pg_locale_t
1189 : 3752823 : pg_newlocale_from_collation(Oid collid)
1190 : : {
1191 : 3752823 : collation_cache_entry *cache_entry;
1192 : 3752823 : bool found;
1193 : :
1194 [ + + ]: 3752823 : if (collid == DEFAULT_COLLATION_OID)
1195 : 3453718 : return default_locale;
1196 : :
1197 : : /*
1198 : : * Some callers expect C_COLLATION_OID to succeed even without catalog
1199 : : * access.
1200 : : */
1201 [ + + ]: 299105 : if (collid == C_COLLATION_OID)
1202 : 293199 : return &c_locale;
1203 : :
1204 [ + - ]: 5906 : if (!OidIsValid(collid))
1205 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
1206 : :
1207 : 5906 : AssertCouldGetRelation();
1208 : :
1209 [ + + ]: 5906 : if (last_collation_cache_oid == collid)
1210 : 5742 : return last_collation_cache_locale;
1211 : :
1212 [ + + ]: 164 : if (CollationCache == NULL)
1213 : : {
1214 : 9 : CollationCacheContext = AllocSetContextCreate(TopMemoryContext,
1215 : : "collation cache",
1216 : : ALLOCSET_DEFAULT_SIZES);
1217 : 9 : CollationCache = collation_cache_create(CollationCacheContext,
1218 : : 16, NULL);
1219 : 9 : }
1220 : :
1221 : 164 : cache_entry = collation_cache_insert(CollationCache, collid, &found);
1222 [ + + ]: 164 : if (!found)
1223 : : {
1224 : : /*
1225 : : * Make sure cache entry is marked invalid, in case we fail before
1226 : : * setting things.
1227 : : */
1228 : 47 : cache_entry->locale = NULL;
1229 : 47 : }
1230 : :
1231 [ + + ]: 164 : if (cache_entry->locale == NULL)
1232 : : {
1233 : 47 : cache_entry->locale = create_pg_locale(collid, CollationCacheContext);
1234 : 47 : }
1235 : :
1236 : 164 : last_collation_cache_oid = collid;
1237 : 164 : last_collation_cache_locale = cache_entry->locale;
1238 : :
1239 : 164 : return cache_entry->locale;
1240 : 3752823 : }
1241 : :
1242 : : /*
1243 : : * Get provider-specific collation version string for the given collation from
1244 : : * the operating system/library.
1245 : : */
1246 : : char *
1247 : 1265 : get_collation_actual_version(char collprovider, const char *collcollate)
1248 : : {
1249 : 1265 : char *collversion = NULL;
1250 : :
1251 [ + + ]: 1265 : if (collprovider == COLLPROVIDER_BUILTIN)
1252 : 13 : collversion = get_collation_actual_version_builtin(collcollate);
1253 : : #ifdef USE_ICU
1254 [ + + ]: 1252 : else if (collprovider == COLLPROVIDER_ICU)
1255 : 921 : collversion = get_collation_actual_version_icu(collcollate);
1256 : : #endif
1257 [ - + ]: 331 : else if (collprovider == COLLPROVIDER_LIBC)
1258 : 331 : collversion = get_collation_actual_version_libc(collcollate);
1259 : :
1260 : 2530 : return collversion;
1261 : 1265 : }
1262 : :
1263 : : /* lowercasing/casefolding in C locale */
1264 : : static size_t
1265 : 762567 : strlower_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
1266 : : {
1267 : 762567 : int i;
1268 : :
1269 [ + - ]: 762567 : srclen = (srclen >= 0) ? srclen : strlen(src);
1270 [ + + + + ]: 7471904 : for (i = 0; i < srclen && i < dstsize; i++)
1271 : 6709337 : dst[i] = pg_ascii_tolower(src[i]);
1272 [ - + ]: 762567 : if (i < dstsize)
1273 : 762567 : dst[i] = '\0';
1274 : 1525134 : return srclen;
1275 : 762567 : }
1276 : :
1277 : : /* titlecasing in C locale */
1278 : : static size_t
1279 : 0 : strtitle_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
1280 : : {
1281 : 0 : bool wasalnum = false;
1282 : 0 : int i;
1283 : :
1284 [ # # ]: 0 : srclen = (srclen >= 0) ? srclen : strlen(src);
1285 [ # # # # ]: 0 : for (i = 0; i < srclen && i < dstsize; i++)
1286 : : {
1287 : 0 : char c = src[i];
1288 : :
1289 [ # # ]: 0 : if (wasalnum)
1290 : 0 : dst[i] = pg_ascii_tolower(c);
1291 : : else
1292 : 0 : dst[i] = pg_ascii_toupper(c);
1293 : :
1294 [ # # # # ]: 0 : wasalnum = ((c >= '0' && c <= '9') ||
1295 [ # # ]: 0 : (c >= 'A' && c <= 'Z') ||
1296 [ # # ]: 0 : (c >= 'a' && c <= 'z'));
1297 : 0 : }
1298 [ # # ]: 0 : if (i < dstsize)
1299 : 0 : dst[i] = '\0';
1300 : 0 : return srclen;
1301 : 0 : }
1302 : :
1303 : : /* uppercasing in C locale */
1304 : : static size_t
1305 : 0 : strupper_c(char *dst, size_t dstsize, const char *src, ssize_t srclen)
1306 : : {
1307 : 0 : int i;
1308 : :
1309 [ # # ]: 0 : srclen = (srclen >= 0) ? srclen : strlen(src);
1310 [ # # # # ]: 0 : for (i = 0; i < srclen && i < dstsize; i++)
1311 : 0 : dst[i] = pg_ascii_toupper(src[i]);
1312 [ # # ]: 0 : if (i < dstsize)
1313 : 0 : dst[i] = '\0';
1314 : 0 : return srclen;
1315 : 0 : }
1316 : :
1317 : : size_t
1318 : 5513 : pg_strlower(char *dst, size_t dstsize, const char *src, ssize_t srclen,
1319 : : pg_locale_t locale)
1320 : : {
1321 [ + - ]: 5513 : if (locale->ctype == NULL)
1322 : 0 : return strlower_c(dst, dstsize, src, srclen);
1323 : : else
1324 : 5513 : return locale->ctype->strlower(dst, dstsize, src, srclen, locale);
1325 : 5513 : }
1326 : :
1327 : : size_t
1328 : 38 : pg_strtitle(char *dst, size_t dstsize, const char *src, ssize_t srclen,
1329 : : pg_locale_t locale)
1330 : : {
1331 [ + - ]: 38 : if (locale->ctype == NULL)
1332 : 0 : return strtitle_c(dst, dstsize, src, srclen);
1333 : : else
1334 : 38 : return locale->ctype->strtitle(dst, dstsize, src, srclen, locale);
1335 : 38 : }
1336 : :
1337 : : size_t
1338 : 158412 : pg_strupper(char *dst, size_t dstsize, const char *src, ssize_t srclen,
1339 : : pg_locale_t locale)
1340 : : {
1341 [ + - ]: 158412 : if (locale->ctype == NULL)
1342 : 0 : return strupper_c(dst, dstsize, src, srclen);
1343 : : else
1344 : 158412 : return locale->ctype->strupper(dst, dstsize, src, srclen, locale);
1345 : 158412 : }
1346 : :
1347 : : size_t
1348 : 4 : pg_strfold(char *dst, size_t dstsize, const char *src, ssize_t srclen,
1349 : : pg_locale_t locale)
1350 : : {
1351 : : /* in the C locale, casefolding is the same as lowercasing */
1352 [ + - ]: 4 : if (locale->ctype == NULL)
1353 : 0 : return strlower_c(dst, dstsize, src, srclen);
1354 : : else
1355 : 4 : return locale->ctype->strfold(dst, dstsize, src, srclen, locale);
1356 : 4 : }
1357 : :
1358 : : /*
1359 : : * Lowercase an identifier using the database default locale.
1360 : : *
1361 : : * For historical reasons, does not use ordinary locale behavior. Should only
1362 : : * be used for identifiers. XXX: can we make this equivalent to
1363 : : * pg_strfold(..., default_locale)?
1364 : : */
1365 : : size_t
1366 : 762567 : pg_downcase_ident(char *dst, size_t dstsize, const char *src, ssize_t srclen)
1367 : : {
1368 : 762567 : pg_locale_t locale = default_locale;
1369 : :
1370 [ + + + - : 762567 : if (locale == NULL || locale->ctype == NULL ||
+ - ]
1371 : 762526 : locale->ctype->downcase_ident == NULL)
1372 : 762567 : return strlower_c(dst, dstsize, src, srclen);
1373 : : else
1374 : 0 : return locale->ctype->downcase_ident(dst, dstsize, src, srclen,
1375 : 0 : locale);
1376 : 762567 : }
1377 : :
1378 : : /*
1379 : : * pg_strcoll
1380 : : *
1381 : : * Like pg_strncoll for NUL-terminated input strings.
1382 : : */
1383 : : int
1384 : 4780310 : pg_strcoll(const char *arg1, const char *arg2, pg_locale_t locale)
1385 : : {
1386 : 4780310 : return locale->collate->strncoll(arg1, -1, arg2, -1, locale);
1387 : : }
1388 : :
1389 : : /*
1390 : : * pg_strncoll
1391 : : *
1392 : : * Call ucol_strcollUTF8(), ucol_strcoll(), strcoll_l() or wcscoll_l() as
1393 : : * appropriate for the given locale, platform, and database encoding. If the
1394 : : * locale is not specified, use the database collation.
1395 : : *
1396 : : * The input strings must be encoded in the database encoding. If an input
1397 : : * string is NUL-terminated, its length may be specified as -1.
1398 : : *
1399 : : * The caller is responsible for breaking ties if the collation is
1400 : : * deterministic; this maintains consistency with pg_strnxfrm(), which cannot
1401 : : * easily account for deterministic collations.
1402 : : */
1403 : : int
1404 : 515688 : pg_strncoll(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2,
1405 : : pg_locale_t locale)
1406 : : {
1407 : 515688 : return locale->collate->strncoll(arg1, len1, arg2, len2, locale);
1408 : : }
1409 : :
1410 : : /*
1411 : : * Return true if the collation provider supports pg_strxfrm() and
1412 : : * pg_strnxfrm(); otherwise false.
1413 : : *
1414 : : *
1415 : : * No similar problem is known for the ICU provider.
1416 : : */
1417 : : bool
1418 : 2852 : pg_strxfrm_enabled(pg_locale_t locale)
1419 : : {
1420 : : /*
1421 : : * locale->collate->strnxfrm is still a required method, even if it may
1422 : : * have the wrong behavior, because the planner uses it for estimates in
1423 : : * some cases.
1424 : : */
1425 : 2852 : return locale->collate->strxfrm_is_safe;
1426 : : }
1427 : :
1428 : : /*
1429 : : * pg_strxfrm
1430 : : *
1431 : : * Like pg_strnxfrm for a NUL-terminated input string.
1432 : : */
1433 : : size_t
1434 : 48 : pg_strxfrm(char *dest, const char *src, size_t destsize, pg_locale_t locale)
1435 : : {
1436 : 48 : return locale->collate->strnxfrm(dest, destsize, src, -1, locale);
1437 : : }
1438 : :
1439 : : /*
1440 : : * pg_strnxfrm
1441 : : *
1442 : : * Transforms 'src' to a nul-terminated string stored in 'dest' such that
1443 : : * ordinary strcmp() on transformed strings is equivalent to pg_strcoll() on
1444 : : * untransformed strings.
1445 : : *
1446 : : * The input string must be encoded in the database encoding. If the input
1447 : : * string is NUL-terminated, its length may be specified as -1. If 'destsize'
1448 : : * is zero, 'dest' may be NULL.
1449 : : *
1450 : : * Not all providers support pg_strnxfrm() safely. The caller should check
1451 : : * pg_strxfrm_enabled() first, otherwise this function may return wrong
1452 : : * results or an error.
1453 : : *
1454 : : * Returns the number of bytes needed (or more) to store the transformed
1455 : : * string, excluding the terminating nul byte. If the value returned is
1456 : : * 'destsize' or greater, the resulting contents of 'dest' are undefined.
1457 : : */
1458 : : size_t
1459 : 958 : pg_strnxfrm(char *dest, size_t destsize, const char *src, ssize_t srclen,
1460 : : pg_locale_t locale)
1461 : : {
1462 : 958 : return locale->collate->strnxfrm(dest, destsize, src, srclen, locale);
1463 : : }
1464 : :
1465 : : /*
1466 : : * Return true if the collation provider supports pg_strxfrm_prefix() and
1467 : : * pg_strnxfrm_prefix(); otherwise false.
1468 : : */
1469 : : bool
1470 : 274 : pg_strxfrm_prefix_enabled(pg_locale_t locale)
1471 : : {
1472 : 274 : return (locale->collate->strnxfrm_prefix != NULL);
1473 : : }
1474 : :
1475 : : /*
1476 : : * pg_strxfrm_prefix
1477 : : *
1478 : : * Like pg_strnxfrm_prefix for a NUL-terminated input string.
1479 : : */
1480 : : size_t
1481 : 274 : pg_strxfrm_prefix(char *dest, const char *src, size_t destsize,
1482 : : pg_locale_t locale)
1483 : : {
1484 : 274 : return locale->collate->strnxfrm_prefix(dest, destsize, src, -1, locale);
1485 : : }
1486 : :
1487 : : /*
1488 : : * pg_strnxfrm_prefix
1489 : : *
1490 : : * Transforms 'src' to a byte sequence stored in 'dest' such that ordinary
1491 : : * memcmp() on the byte sequence is equivalent to pg_strncoll() on
1492 : : * untransformed strings. The result is not nul-terminated.
1493 : : *
1494 : : * The input string must be encoded in the database encoding. If the input
1495 : : * string is NUL-terminated, its length may be specified as -1.
1496 : : *
1497 : : * Not all providers support pg_strnxfrm_prefix() safely. The caller should
1498 : : * check pg_strxfrm_prefix_enabled() first, otherwise this function may return
1499 : : * wrong results or an error.
1500 : : *
1501 : : * If destsize is not large enough to hold the resulting byte sequence, stores
1502 : : * only the first destsize bytes in 'dest'. Returns the number of bytes
1503 : : * actually copied to 'dest'.
1504 : : */
1505 : : size_t
1506 : 0 : pg_strnxfrm_prefix(char *dest, size_t destsize, const char *src,
1507 : : ssize_t srclen, pg_locale_t locale)
1508 : : {
1509 : 0 : return locale->collate->strnxfrm_prefix(dest, destsize, src, srclen, locale);
1510 : : }
1511 : :
1512 : : bool
1513 : 6182 : pg_iswdigit(pg_wchar wc, pg_locale_t locale)
1514 : : {
1515 [ + - ]: 6182 : if (locale->ctype == NULL)
1516 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1517 : 0 : (pg_char_properties[wc] & PG_ISDIGIT));
1518 : : else
1519 : 6182 : return locale->ctype->wc_isdigit(wc, locale);
1520 : 6182 : }
1521 : :
1522 : : bool
1523 : 17147 : pg_iswalpha(pg_wchar wc, pg_locale_t locale)
1524 : : {
1525 [ + - ]: 17147 : if (locale->ctype == NULL)
1526 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1527 : 0 : (pg_char_properties[wc] & PG_ISALPHA));
1528 : : else
1529 : 17147 : return locale->ctype->wc_isalpha(wc, locale);
1530 : 17147 : }
1531 : :
1532 : : bool
1533 : 2179 : pg_iswalnum(pg_wchar wc, pg_locale_t locale)
1534 : : {
1535 [ + - ]: 2179 : if (locale->ctype == NULL)
1536 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1537 : 0 : (pg_char_properties[wc] & PG_ISALNUM));
1538 : : else
1539 : 2179 : return locale->ctype->wc_isalnum(wc, locale);
1540 : 2179 : }
1541 : :
1542 : : bool
1543 : 0 : pg_iswupper(pg_wchar wc, pg_locale_t locale)
1544 : : {
1545 [ # # ]: 0 : if (locale->ctype == NULL)
1546 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1547 : 0 : (pg_char_properties[wc] & PG_ISUPPER));
1548 : : else
1549 : 0 : return locale->ctype->wc_isupper(wc, locale);
1550 : 0 : }
1551 : :
1552 : : bool
1553 : 0 : pg_iswlower(pg_wchar wc, pg_locale_t locale)
1554 : : {
1555 [ # # ]: 0 : if (locale->ctype == NULL)
1556 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1557 : 0 : (pg_char_properties[wc] & PG_ISLOWER));
1558 : : else
1559 : 0 : return locale->ctype->wc_islower(wc, locale);
1560 : 0 : }
1561 : :
1562 : : bool
1563 : 0 : pg_iswgraph(pg_wchar wc, pg_locale_t locale)
1564 : : {
1565 [ # # ]: 0 : if (locale->ctype == NULL)
1566 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1567 : 0 : (pg_char_properties[wc] & PG_ISGRAPH));
1568 : : else
1569 : 0 : return locale->ctype->wc_isgraph(wc, locale);
1570 : 0 : }
1571 : :
1572 : : bool
1573 : 0 : pg_iswprint(pg_wchar wc, pg_locale_t locale)
1574 : : {
1575 [ # # ]: 0 : if (locale->ctype == NULL)
1576 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1577 : 0 : (pg_char_properties[wc] & PG_ISPRINT));
1578 : : else
1579 : 0 : return locale->ctype->wc_isprint(wc, locale);
1580 : 0 : }
1581 : :
1582 : : bool
1583 : 0 : pg_iswpunct(pg_wchar wc, pg_locale_t locale)
1584 : : {
1585 [ # # ]: 0 : if (locale->ctype == NULL)
1586 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1587 : 0 : (pg_char_properties[wc] & PG_ISPUNCT));
1588 : : else
1589 : 0 : return locale->ctype->wc_ispunct(wc, locale);
1590 : 0 : }
1591 : :
1592 : : bool
1593 : 113 : pg_iswspace(pg_wchar wc, pg_locale_t locale)
1594 : : {
1595 [ + - ]: 113 : if (locale->ctype == NULL)
1596 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1597 : 0 : (pg_char_properties[wc] & PG_ISSPACE));
1598 : : else
1599 : 113 : return locale->ctype->wc_isspace(wc, locale);
1600 : 113 : }
1601 : :
1602 : : bool
1603 : 3 : pg_iswxdigit(pg_wchar wc, pg_locale_t locale)
1604 : : {
1605 [ + - ]: 3 : if (locale->ctype == NULL)
1606 [ # # ]: 0 : return (wc <= (pg_wchar) 127 &&
1607 [ # # ]: 0 : ((pg_char_properties[wc] & PG_ISDIGIT) ||
1608 [ # # # # ]: 0 : ((wc >= 'A' && wc <= 'F') ||
1609 [ # # ]: 0 : (wc >= 'a' && wc <= 'f'))));
1610 : : else
1611 : 3 : return locale->ctype->wc_isxdigit(wc, locale);
1612 : 3 : }
1613 : :
1614 : : bool
1615 : 43 : pg_iswcased(pg_wchar wc, pg_locale_t locale)
1616 : : {
1617 : : /* for the C locale, Cased and Alpha are equivalent */
1618 [ + + ]: 43 : if (locale->ctype == NULL)
1619 [ - + ]: 22 : return (wc <= (pg_wchar) 127 &&
1620 : 22 : (pg_char_properties[wc] & PG_ISALPHA));
1621 : : else
1622 : 21 : return locale->ctype->wc_iscased(wc, locale);
1623 : 43 : }
1624 : :
1625 : : pg_wchar
1626 : 0 : pg_towupper(pg_wchar wc, pg_locale_t locale)
1627 : : {
1628 [ # # ]: 0 : if (locale->ctype == NULL)
1629 : : {
1630 [ # # ]: 0 : if (wc <= (pg_wchar) 127)
1631 : 0 : return pg_ascii_toupper((unsigned char) wc);
1632 : 0 : return wc;
1633 : : }
1634 : : else
1635 : 0 : return locale->ctype->wc_toupper(wc, locale);
1636 : 0 : }
1637 : :
1638 : : pg_wchar
1639 : 0 : pg_towlower(pg_wchar wc, pg_locale_t locale)
1640 : : {
1641 [ # # ]: 0 : if (locale->ctype == NULL)
1642 : : {
1643 [ # # ]: 0 : if (wc <= (pg_wchar) 127)
1644 : 0 : return pg_ascii_tolower((unsigned char) wc);
1645 : 0 : return wc;
1646 : : }
1647 : : else
1648 : 0 : return locale->ctype->wc_tolower(wc, locale);
1649 : 0 : }
1650 : :
1651 : : /* version of Unicode used by ICU */
1652 : : const char *
1653 : 0 : pg_icu_unicode_version()
1654 : : {
1655 : : #ifdef USE_ICU
1656 : 0 : return U_UNICODE_VERSION;
1657 : : #else
1658 : : return NULL;
1659 : : #endif
1660 : : }
1661 : :
1662 : : /*
1663 : : * Return required encoding ID for the given locale, or -1 if any encoding is
1664 : : * valid for the locale.
1665 : : */
1666 : : int
1667 : 18 : builtin_locale_encoding(const char *locale)
1668 : : {
1669 [ + + ]: 18 : if (strcmp(locale, "C") == 0)
1670 : 7 : return -1;
1671 [ + + ]: 11 : else if (strcmp(locale, "C.UTF-8") == 0)
1672 : 7 : return PG_UTF8;
1673 [ + - ]: 4 : else if (strcmp(locale, "PG_UNICODE_FAST") == 0)
1674 : 4 : return PG_UTF8;
1675 : :
1676 : :
1677 [ # # # # ]: 0 : ereport(ERROR,
1678 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1679 : : errmsg("invalid locale name \"%s\" for builtin provider",
1680 : : locale)));
1681 : :
1682 : 0 : return 0; /* keep compiler quiet */
1683 : 18 : }
1684 : :
1685 : :
1686 : : /*
1687 : : * Validate the locale and encoding combination, and return the canonical form
1688 : : * of the locale name.
1689 : : */
1690 : : const char *
1691 : 16 : builtin_validate_locale(int encoding, const char *locale)
1692 : : {
1693 : 16 : const char *canonical_name = NULL;
1694 : 16 : int required_encoding;
1695 : :
1696 [ + + ]: 16 : if (strcmp(locale, "C") == 0)
1697 : 5 : canonical_name = "C";
1698 [ + + + + ]: 11 : else if (strcmp(locale, "C.UTF-8") == 0 || strcmp(locale, "C.UTF8") == 0)
1699 : 5 : canonical_name = "C.UTF-8";
1700 [ + + ]: 6 : else if (strcmp(locale, "PG_UNICODE_FAST") == 0)
1701 : 3 : canonical_name = "PG_UNICODE_FAST";
1702 : :
1703 [ + + ]: 16 : if (!canonical_name)
1704 [ + - + - ]: 3 : ereport(ERROR,
1705 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1706 : : errmsg("invalid locale name \"%s\" for builtin provider",
1707 : : locale)));
1708 : :
1709 : 13 : required_encoding = builtin_locale_encoding(canonical_name);
1710 [ + + + - ]: 13 : if (required_encoding >= 0 && encoding != required_encoding)
1711 [ # # # # ]: 0 : ereport(ERROR,
1712 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1713 : : errmsg("encoding \"%s\" does not match locale \"%s\"",
1714 : : pg_encoding_to_char(encoding), locale)));
1715 : :
1716 : 26 : return canonical_name;
1717 : 13 : }
1718 : :
1719 : :
1720 : :
1721 : : /*
1722 : : * Return the BCP47 language tag representation of the requested locale.
1723 : : *
1724 : : * This function should be called before passing the string to ucol_open(),
1725 : : * because conversion to a language tag also performs "level 2
1726 : : * canonicalization". In addition to producing a consistent format, level 2
1727 : : * canonicalization is able to more accurately interpret different input
1728 : : * locale string formats, such as POSIX and .NET IDs.
1729 : : */
1730 : : char *
1731 : 894 : icu_language_tag(const char *loc_str, int elevel)
1732 : : {
1733 : : #ifdef USE_ICU
1734 : 894 : UErrorCode status;
1735 : 894 : char *langtag;
1736 : 894 : size_t buflen = 32; /* arbitrary starting buffer size */
1737 : 894 : const bool strict = true;
1738 : :
1739 : : /*
1740 : : * A BCP47 language tag doesn't have a clearly-defined upper limit (cf.
1741 : : * RFC5646 section 4.4). Additionally, in older ICU versions,
1742 : : * uloc_toLanguageTag() doesn't always return the ultimate length on the
1743 : : * first call, necessitating a loop.
1744 : : */
1745 : 894 : langtag = palloc(buflen);
1746 : 894 : while (true)
1747 : : {
1748 : 894 : status = U_ZERO_ERROR;
1749 : 894 : uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status);
1750 : :
1751 : : /* try again if the buffer is not large enough */
1752 [ + + ]: 894 : if ((status == U_BUFFER_OVERFLOW_ERROR ||
1753 [ + - ]: 894 : status == U_STRING_NOT_TERMINATED_WARNING) &&
1754 : 894 : buflen < MaxAllocSize)
1755 : : {
1756 [ # # ]: 0 : buflen = Min(buflen * 2, MaxAllocSize);
1757 : 0 : langtag = repalloc(langtag, buflen);
1758 : 0 : continue;
1759 : : }
1760 : :
1761 : 894 : break;
1762 : : }
1763 : :
1764 [ + + ]: 894 : if (U_FAILURE(status))
1765 : : {
1766 : 3 : pfree(langtag);
1767 : :
1768 [ - + ]: 3 : if (elevel > 0)
1769 [ - + # # : 3 : ereport(elevel,
+ + - + #
# ]
1770 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1771 : : errmsg("could not convert locale name \"%s\" to language tag: %s",
1772 : : loc_str, u_errorName(status))));
1773 : 1 : return NULL;
1774 : : }
1775 : :
1776 : 891 : return langtag;
1777 : : #else /* not USE_ICU */
1778 : : ereport(ERROR,
1779 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1780 : : errmsg("ICU is not supported in this build")));
1781 : : return NULL; /* keep compiler quiet */
1782 : : #endif /* not USE_ICU */
1783 : 892 : }
1784 : :
1785 : : /*
1786 : : * Perform best-effort check that the locale is a valid one.
1787 : : */
1788 : : void
1789 : 23 : icu_validate_locale(const char *loc_str)
1790 : : {
1791 : : #ifdef USE_ICU
1792 : 23 : UCollator *collator;
1793 : 23 : UErrorCode status;
1794 : 23 : char lang[ULOC_LANG_CAPACITY];
1795 : 23 : bool found = false;
1796 : 23 : int elevel = icu_validation_level;
1797 : :
1798 : : /* no validation */
1799 [ + + ]: 23 : if (elevel < 0)
1800 : 2 : return;
1801 : :
1802 : : /* downgrade to WARNING during pg_upgrade */
1803 [ - + # # ]: 21 : if (IsBinaryUpgrade && elevel > WARNING)
1804 : 0 : elevel = WARNING;
1805 : :
1806 : : /* validate that we can extract the language */
1807 : 21 : status = U_ZERO_ERROR;
1808 : 21 : uloc_getLanguage(loc_str, lang, ULOC_LANG_CAPACITY, &status);
1809 [ + + + + ]: 21 : if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1810 : : {
1811 [ # # # # : 2 : ereport(elevel,
# # # # #
# ]
1812 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1813 : : errmsg("could not get language from ICU locale \"%s\": %s",
1814 : : loc_str, u_errorName(status)),
1815 : : errhint("To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1816 : : "icu_validation_level", "disabled")));
1817 : 0 : return;
1818 : : }
1819 : :
1820 : : /* check for special language name */
1821 [ + + ]: 19 : if (strcmp(lang, "") == 0 ||
1822 [ + - + + ]: 4 : strcmp(lang, "root") == 0 || strcmp(lang, "und") == 0)
1823 : 16 : found = true;
1824 : :
1825 : : /* search for matching language within ICU */
1826 [ + + + + ]: 2017 : for (int32_t i = 0; !found && i < uloc_countAvailable(); i++)
1827 : : {
1828 : 1996 : const char *otherloc = uloc_getAvailable(i);
1829 : 1996 : char otherlang[ULOC_LANG_CAPACITY];
1830 : :
1831 : 1996 : status = U_ZERO_ERROR;
1832 : 1996 : uloc_getLanguage(otherloc, otherlang, ULOC_LANG_CAPACITY, &status);
1833 [ + - - + ]: 1996 : if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING)
1834 : 0 : continue;
1835 : :
1836 [ + + ]: 1996 : if (strcmp(lang, otherlang) == 0)
1837 : 2 : found = true;
1838 [ - + ]: 1996 : }
1839 : :
1840 [ + + ]: 21 : if (!found)
1841 [ - + # # : 3 : ereport(elevel,
+ + - + #
# ]
1842 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1843 : : errmsg("ICU locale \"%s\" has unknown language \"%s\"",
1844 : : loc_str, lang),
1845 : : errhint("To disable ICU locale validation, set the parameter \"%s\" to \"%s\".",
1846 : : "icu_validation_level", "disabled")));
1847 : :
1848 : : /* check that it can be opened */
1849 : 19 : collator = pg_ucol_open(loc_str);
1850 : 19 : ucol_close(collator);
1851 : : #else /* not USE_ICU */
1852 : : /* could get here if a collation was created by a build with ICU */
1853 : : ereport(ERROR,
1854 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1855 : : errmsg("ICU is not supported in this build")));
1856 : : #endif /* not USE_ICU */
1857 : 21 : }
|