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