PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_locale_libc.c
Go to the documentation of this file.
1/*-----------------------------------------------------------------------
2 *
3 * PostgreSQL locale utilities for libc
4 *
5 * Portions Copyright (c) 2002-2026, PostgreSQL Global Development Group
6 *
7 * src/backend/utils/adt/pg_locale_libc.c
8 *
9 *-----------------------------------------------------------------------
10 */
11
12#include "postgres.h"
13
14#include <limits.h>
15#include <wctype.h>
16
17#include "access/htup_details.h"
18#include "catalog/pg_database.h"
20#include "mb/pg_wchar.h"
21#include "miscadmin.h"
22#include "utils/builtins.h"
23#include "utils/formatting.h"
24#include "utils/memutils.h"
25#include "utils/pg_locale.h"
26#include "utils/syscache.h"
27
28#ifdef __GLIBC__
29#include <gnu/libc-version.h>
30#endif
31
32#ifdef WIN32
33#include <shlwapi.h>
34#endif
35
36/*
37 * For the libc provider, to provide as much functionality as possible on a
38 * variety of platforms without going so far as to implement everything from
39 * scratch, we use several implementation strategies depending on the
40 * situation:
41 *
42 * 1. In C/POSIX collations, we use hard-wired code. We can't depend on
43 * the <ctype.h> functions since those will obey LC_CTYPE. Note that these
44 * collations don't give a fig about multibyte characters.
45 *
46 * 2. When working in UTF8 encoding, we use the <wctype.h> functions.
47 * This assumes that every platform uses Unicode codepoints directly
48 * as the wchar_t representation of Unicode. On some platforms
49 * wchar_t is only 16 bits wide, so we have to punt for codepoints > 0xFFFF.
50 *
51 * 3. In all other encodings, we use the <ctype.h> functions for pg_wchar
52 * values up to 255, and punt for values above that. This is 100% correct
53 * only in single-byte encodings such as LATINn. However, non-Unicode
54 * multibyte encodings are mostly Far Eastern character sets for which the
55 * properties being tested here aren't very relevant for higher code values
56 * anyway. The difficulty with using the <wctype.h> functions with
57 * non-Unicode multibyte encodings is that we can have no certainty that
58 * the platform's wchar_t representation matches what we do in pg_wchar
59 * conversions.
60 *
61 * As a special case, in the "default" collation, (2) and (3) force ASCII
62 * letters to follow ASCII upcase/downcase rules, while in a non-default
63 * collation we just let the library functions do what they will. The case
64 * where this matters is treatment of I/i in Turkish, and the behavior is
65 * meant to match the upper()/lower() SQL functions.
66 *
67 * NB: the coding here assumes pg_wchar is an unsigned type.
68 */
69
70/*
71 * Size of stack buffer to use for string transformations, used to avoid heap
72 * allocations in typical cases. This should be large enough that most strings
73 * will fit, but small enough that we feel comfortable putting it on the
74 * stack.
75 */
76#define TEXTBUFLEN 1024
77
79
80static int strncoll_libc(const char *arg1, size_t len1,
81 const char *arg2, size_t len2,
82 pg_locale_t locale);
83static int strcoll_libc(const char *arg1, const char *arg2,
84 pg_locale_t locale);
85static size_t strnxfrm_libc(char *dest, size_t destsize,
86 const char *src, size_t srclen,
87 pg_locale_t locale);
88static size_t strxfrm_libc(char *dest, size_t destsize,
89 const char *src, pg_locale_t locale);
90extern char *get_collation_actual_version_libc(const char *collcollate);
91static locale_t make_libc_collator(const char *collate,
92 const char *ctype);
93
94#ifdef WIN32
95static int strncoll_libc_win32_utf8(const char *arg1, size_t len1,
96 const char *arg2, size_t len2,
97 pg_locale_t locale);
98static int strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
99 pg_locale_t locale);
100#endif
101
102static size_t char2wchar(wchar_t *to, size_t tolen, const char *from,
103 size_t fromlen, locale_t loc);
104
105static size_t strlower_libc_sb(char *dest, size_t destsize,
106 const char *src, size_t srclen,
107 pg_locale_t locale);
108static size_t strlower_libc_mb(char *dest, size_t destsize,
109 const char *src, size_t srclen,
110 pg_locale_t locale);
111static size_t strtitle_libc_sb(char *dest, size_t destsize,
112 const char *src, size_t srclen,
113 pg_locale_t locale);
114static size_t strtitle_libc_mb(char *dest, size_t destsize,
115 const char *src, size_t srclen,
116 pg_locale_t locale);
117static size_t strupper_libc_sb(char *dest, size_t destsize,
118 const char *src, size_t srclen,
119 pg_locale_t locale);
120static size_t strupper_libc_mb(char *dest, size_t destsize,
121 const char *src, size_t srclen,
122 pg_locale_t locale);
123
124static bool
126{
127 if (wc > UCHAR_MAX)
128 return false;
129 return isdigit_l((unsigned char) wc, locale->lt);
130}
131
132static bool
134{
135 if (wc > UCHAR_MAX)
136 return false;
137 return isalpha_l((unsigned char) wc, locale->lt);
138}
139
140static bool
142{
143 if (wc > UCHAR_MAX)
144 return false;
145 return isalnum_l((unsigned char) wc, locale->lt);
146}
147
148static bool
150{
151 if (wc > UCHAR_MAX)
152 return false;
153 return isupper_l((unsigned char) wc, locale->lt);
154}
155
156static bool
158{
159 if (wc > UCHAR_MAX)
160 return false;
161 return islower_l((unsigned char) wc, locale->lt);
162}
163
164static bool
166{
167 if (wc > UCHAR_MAX)
168 return false;
169 return isgraph_l((unsigned char) wc, locale->lt);
170}
171
172static bool
174{
175 if (wc > UCHAR_MAX)
176 return false;
177 return isprint_l((unsigned char) wc, locale->lt);
178}
179
180static bool
182{
183 if (wc > UCHAR_MAX)
184 return false;
185 return ispunct_l((unsigned char) wc, locale->lt);
186}
187
188static bool
190{
191 if (wc > UCHAR_MAX)
192 return false;
193 return isspace_l((unsigned char) wc, locale->lt);
194}
195
196static bool
198{
199 if (wc > UCHAR_MAX)
200 return false;
201#ifndef WIN32
202 return isxdigit_l((unsigned char) wc, locale->lt);
203#else
204 return _isxdigit_l((unsigned char) wc, locale->lt);
205#endif
206}
207
208static bool
210{
211 if (wc > UCHAR_MAX)
212 return false;
213 return isupper_l((unsigned char) wc, locale->lt) ||
214 islower_l((unsigned char) wc, locale->lt);
215}
216
217static bool
219{
220 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
221 return false;
222 return iswdigit_l((wint_t) wc, locale->lt);
223}
224
225static bool
227{
228 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
229 return false;
230 return iswalpha_l((wint_t) wc, locale->lt);
231}
232
233static bool
235{
236 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
237 return false;
238 return iswalnum_l((wint_t) wc, locale->lt);
239}
240
241static bool
243{
244 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
245 return false;
246 return iswupper_l((wint_t) wc, locale->lt);
247}
248
249static bool
251{
252 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
253 return false;
254 return iswlower_l((wint_t) wc, locale->lt);
255}
256
257static bool
259{
260 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
261 return false;
262 return iswgraph_l((wint_t) wc, locale->lt);
263}
264
265static bool
267{
268 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
269 return false;
270 return iswprint_l((wint_t) wc, locale->lt);
271}
272
273static bool
275{
276 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
277 return false;
278 return iswpunct_l((wint_t) wc, locale->lt);
279}
280
281static bool
283{
284 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
285 return false;
286 return iswspace_l((wint_t) wc, locale->lt);
287}
288
289static bool
291{
292 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
293 return false;
294#ifndef WIN32
295 return iswxdigit_l((wint_t) wc, locale->lt);
296#else
297 return _iswxdigit_l((wint_t) wc, locale->lt);
298#endif
299}
300
301static bool
303{
304 if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
305 return false;
306 return iswupper_l((wint_t) wc, locale->lt) ||
307 iswlower_l((wint_t) wc, locale->lt);
308}
309
310static pg_wchar
312{
314
315 /* force C behavior for ASCII characters, per comments above */
316 if (locale->is_default && wc <= (pg_wchar) 127)
317 return pg_ascii_toupper((unsigned char) wc);
318 else if (wc <= (pg_wchar) UCHAR_MAX)
319 return toupper_l((unsigned char) wc, locale->lt);
320 else
321 return wc;
322}
323
324static pg_wchar
326{
328
329 /* force C behavior for ASCII characters, per comments above */
330 if (locale->is_default && wc <= (pg_wchar) 127)
331 return pg_ascii_toupper((unsigned char) wc);
332 else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
333 return towupper_l((wint_t) wc, locale->lt);
334 else
335 return wc;
336}
337
338static pg_wchar
340{
342
343 /* force C behavior for ASCII characters, per comments above */
344 if (locale->is_default && wc <= (pg_wchar) 127)
345 return pg_ascii_tolower((unsigned char) wc);
346 else if (wc <= (pg_wchar) UCHAR_MAX)
347 return tolower_l((unsigned char) wc, locale->lt);
348 else
349 return wc;
350}
351
352static pg_wchar
354{
356
357 /* force C behavior for ASCII characters, per comments above */
358 if (locale->is_default && wc <= (pg_wchar) 127)
359 return pg_ascii_tolower((unsigned char) wc);
360 else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
361 return towlower_l((wint_t) wc, locale->lt);
362 else
363 return wc;
364}
365
366/*
367 * Characters A..Z always downcase to a..z, even in the Turkish
368 * locale. Characters beyond 127 use tolower().
369 */
370static size_t
371downcase_ident_libc_sb(char *dst, size_t dstsize, const char *src,
372 size_t srclen, pg_locale_t locale)
373{
374 locale_t loc = locale->lt;
375 size_t i;
376
377 for (i = 0; i < srclen && i < dstsize; i++)
378 {
379 unsigned char ch = (unsigned char) src[i];
380
381 if (ch >= 'A' && ch <= 'Z')
383 else if (IS_HIGHBIT_SET(ch) && isupper_l(ch, loc))
384 ch = tolower_l(ch, loc);
385 dst[i] = (char) ch;
386 }
387
388 if (i < dstsize)
389 dst[i] = '\0';
390
391 return srclen;
392}
393
396 .strtitle = strtitle_libc_sb,
397 .strupper = strupper_libc_sb,
398 /* in libc, casefolding is the same as lowercasing */
399 .strfold = strlower_libc_sb,
400 .downcase_ident = downcase_ident_libc_sb,
401 .wc_isdigit = wc_isdigit_libc_sb,
402 .wc_isalpha = wc_isalpha_libc_sb,
403 .wc_isalnum = wc_isalnum_libc_sb,
404 .wc_isupper = wc_isupper_libc_sb,
405 .wc_islower = wc_islower_libc_sb,
406 .wc_isgraph = wc_isgraph_libc_sb,
407 .wc_isprint = wc_isprint_libc_sb,
408 .wc_ispunct = wc_ispunct_libc_sb,
409 .wc_isspace = wc_isspace_libc_sb,
410 .wc_isxdigit = wc_isxdigit_libc_sb,
411 .wc_iscased = wc_iscased_libc_sb,
412 .wc_toupper = toupper_libc_sb,
413 .wc_tolower = tolower_libc_sb,
414};
415
416/*
417 * Non-UTF8 multibyte encodings use multibyte semantics for case mapping, but
418 * single-byte semantics for pattern matching.
419 */
422 .strtitle = strtitle_libc_mb,
423 .strupper = strupper_libc_mb,
424 /* in libc, casefolding is the same as lowercasing */
425 .strfold = strlower_libc_mb,
426 /* uses plain ASCII semantics for historical reasons */
427 .downcase_ident = NULL,
428 .wc_isdigit = wc_isdigit_libc_sb,
429 .wc_isalpha = wc_isalpha_libc_sb,
430 .wc_isalnum = wc_isalnum_libc_sb,
431 .wc_isupper = wc_isupper_libc_sb,
432 .wc_islower = wc_islower_libc_sb,
433 .wc_isgraph = wc_isgraph_libc_sb,
434 .wc_isprint = wc_isprint_libc_sb,
435 .wc_ispunct = wc_ispunct_libc_sb,
436 .wc_isspace = wc_isspace_libc_sb,
437 .wc_isxdigit = wc_isxdigit_libc_sb,
438 .wc_iscased = wc_iscased_libc_sb,
439 .wc_toupper = toupper_libc_sb,
440 .wc_tolower = tolower_libc_sb,
441};
442
445 .strtitle = strtitle_libc_mb,
446 .strupper = strupper_libc_mb,
447 /* in libc, casefolding is the same as lowercasing */
448 .strfold = strlower_libc_mb,
449 /* uses plain ASCII semantics for historical reasons */
450 .downcase_ident = NULL,
451 .wc_isdigit = wc_isdigit_libc_mb,
452 .wc_isalpha = wc_isalpha_libc_mb,
453 .wc_isalnum = wc_isalnum_libc_mb,
454 .wc_isupper = wc_isupper_libc_mb,
455 .wc_islower = wc_islower_libc_mb,
456 .wc_isgraph = wc_isgraph_libc_mb,
457 .wc_isprint = wc_isprint_libc_mb,
458 .wc_ispunct = wc_ispunct_libc_mb,
459 .wc_isspace = wc_isspace_libc_mb,
460 .wc_isxdigit = wc_isxdigit_libc_mb,
461 .wc_iscased = wc_iscased_libc_mb,
462 .wc_toupper = toupper_libc_mb,
463 .wc_tolower = tolower_libc_mb,
464};
465
468 .strcoll = strcoll_libc,
469 .strnxfrm = strnxfrm_libc,
470 .strxfrm = strxfrm_libc,
471 .strnxfrm_prefix = NULL,
472 .strxfrm_prefix = NULL,
473
474 /*
475 * Unfortunately, it seems that strxfrm() for non-C collations is broken
476 * on many common platforms; testing of multiple versions of glibc reveals
477 * that, for many locales, strcoll() and strxfrm() do not return
478 * consistent results. While no other libc other than Cygwin has so far
479 * been shown to have a problem, we take the conservative course of action
480 * for right now and disable this categorically. (Users who are certain
481 * this isn't a problem on their system can define TRUST_STRXFRM.)
482 */
483#ifdef TRUST_STRXFRM
484 .strxfrm_is_safe = true,
485#else
486 .strxfrm_is_safe = false,
487#endif
488};
489
490#ifdef WIN32
493 .strcoll = strcoll_libc_win32_utf8,
494 .strnxfrm = strnxfrm_libc,
495 .strxfrm = strxfrm_libc,
496 .strnxfrm_prefix = NULL,
497#ifdef TRUST_STRXFRM
498 .strxfrm_is_safe = true,
499#else
500 .strxfrm_is_safe = false,
501#endif
502};
503#endif
504
505static size_t
506strlower_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
507 pg_locale_t locale)
508{
509 if (srclen + 1 <= destsize)
510 {
511 locale_t loc = locale->lt;
512 char *p;
513
514 memcpy(dest, src, srclen);
515 dest[srclen] = '\0';
516
517 /*
518 * Note: we assume that tolower_l() will not be so broken as to need
519 * an isupper_l() guard test. When using the default collation, we
520 * apply the traditional Postgres behavior that forces ASCII-style
521 * treatment of I/i, but in non-default collations you get exactly
522 * what the collation says.
523 */
524 for (p = dest; *p; p++)
525 {
526 if (locale->is_default)
527 {
528 if (*p >= 'A' && *p <= 'Z')
529 *p += 'a' - 'A';
530 else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
531 *p = tolower_l((unsigned char) *p, loc);
532 }
533 else
534 *p = tolower_l((unsigned char) *p, loc);
535 }
536 }
537
538 return srclen;
539}
540
541static size_t
542strlower_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
543 pg_locale_t locale)
544{
545 locale_t loc = locale->lt;
546 size_t result_size;
547 wchar_t *workspace;
548 char *result;
549 size_t curr_char;
550 size_t max_size;
551
552 /* Overflow paranoia */
553 if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
556 errmsg("out of memory")));
557
558 /* Output workspace cannot have more codes than input bytes */
559 workspace = palloc_array(wchar_t, srclen + 1);
560
561 char2wchar(workspace, srclen + 1, src, srclen, loc);
562
563 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
564 workspace[curr_char] = towlower_l(workspace[curr_char], loc);
565
566 /*
567 * Make result large enough; case change might change number of bytes
568 */
570 result = palloc(max_size + 1);
571
572 result_size = wchar2char(result, workspace, max_size + 1, loc);
573
574 if (destsize >= result_size + 1)
575 {
576 memcpy(dest, result, result_size);
577 dest[result_size] = '\0';
578 }
579
580 pfree(workspace);
581 pfree(result);
582
583 return result_size;
584}
585
586static size_t
587strtitle_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
588 pg_locale_t locale)
589{
590 if (srclen + 1 <= destsize)
591 {
592 locale_t loc = locale->lt;
593 int wasalnum = false;
594 char *p;
595
596 memcpy(dest, src, srclen);
597 dest[srclen] = '\0';
598
599 /*
600 * Note: we assume that toupper_l()/tolower_l() will not be so broken
601 * as to need guard tests. When using the default collation, we apply
602 * the traditional Postgres behavior that forces ASCII-style treatment
603 * of I/i, but in non-default collations you get exactly what the
604 * collation says.
605 */
606 for (p = dest; *p; p++)
607 {
608 if (locale->is_default)
609 {
610 if (wasalnum)
611 {
612 if (*p >= 'A' && *p <= 'Z')
613 *p += 'a' - 'A';
614 else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
615 *p = tolower_l((unsigned char) *p, loc);
616 }
617 else
618 {
619 if (*p >= 'a' && *p <= 'z')
620 *p -= 'a' - 'A';
621 else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
622 *p = toupper_l((unsigned char) *p, loc);
623 }
624 }
625 else
626 {
627 if (wasalnum)
628 *p = tolower_l((unsigned char) *p, loc);
629 else
630 *p = toupper_l((unsigned char) *p, loc);
631 }
632 wasalnum = isalnum_l((unsigned char) *p, loc);
633 }
634 }
635
636 return srclen;
637}
638
639static size_t
640strtitle_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
641 pg_locale_t locale)
642{
643 locale_t loc = locale->lt;
644 int wasalnum = false;
645 size_t result_size;
646 wchar_t *workspace;
647 char *result;
648 size_t curr_char;
649 size_t max_size;
650
651 /* Overflow paranoia */
652 if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
655 errmsg("out of memory")));
656
657 /* Output workspace cannot have more codes than input bytes */
658 workspace = palloc_array(wchar_t, srclen + 1);
659
660 char2wchar(workspace, srclen + 1, src, srclen, loc);
661
662 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
663 {
664 if (wasalnum)
665 workspace[curr_char] = towlower_l(workspace[curr_char], loc);
666 else
667 workspace[curr_char] = towupper_l(workspace[curr_char], loc);
668 wasalnum = iswalnum_l(workspace[curr_char], loc);
669 }
670
671 /*
672 * Make result large enough; case change might change number of bytes
673 */
675 result = palloc(max_size + 1);
676
677 result_size = wchar2char(result, workspace, max_size + 1, loc);
678
679 if (destsize >= result_size + 1)
680 {
681 memcpy(dest, result, result_size);
682 dest[result_size] = '\0';
683 }
684
685 pfree(workspace);
686 pfree(result);
687
688 return result_size;
689}
690
691static size_t
692strupper_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
693 pg_locale_t locale)
694{
695 if (srclen + 1 <= destsize)
696 {
697 locale_t loc = locale->lt;
698 char *p;
699
700 memcpy(dest, src, srclen);
701 dest[srclen] = '\0';
702
703 /*
704 * Note: we assume that toupper_l() will not be so broken as to need
705 * an islower_l() guard test. When using the default collation, we
706 * apply the traditional Postgres behavior that forces ASCII-style
707 * treatment of I/i, but in non-default collations you get exactly
708 * what the collation says.
709 */
710 for (p = dest; *p; p++)
711 {
712 if (locale->is_default)
713 {
714 if (*p >= 'a' && *p <= 'z')
715 *p -= 'a' - 'A';
716 else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
717 *p = toupper_l((unsigned char) *p, loc);
718 }
719 else
720 *p = toupper_l((unsigned char) *p, loc);
721 }
722 }
723
724 return srclen;
725}
726
727static size_t
728strupper_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
729 pg_locale_t locale)
730{
731 locale_t loc = locale->lt;
732 size_t result_size;
733 wchar_t *workspace;
734 char *result;
735 size_t curr_char;
736 size_t max_size;
737
738 /* Overflow paranoia */
739 if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
742 errmsg("out of memory")));
743
744 /* Output workspace cannot have more codes than input bytes */
745 workspace = palloc_array(wchar_t, srclen + 1);
746
747 char2wchar(workspace, srclen + 1, src, srclen, loc);
748
749 for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
750 workspace[curr_char] = towupper_l(workspace[curr_char], loc);
751
752 /*
753 * Make result large enough; case change might change number of bytes
754 */
756 result = palloc(max_size + 1);
757
758 result_size = wchar2char(result, workspace, max_size + 1, loc);
759
760 if (destsize >= result_size + 1)
761 {
762 memcpy(dest, result, result_size);
763 dest[result_size] = '\0';
764 }
765
766 pfree(workspace);
767 pfree(result);
768
769 return result_size;
770}
771
774{
775 const char *collate;
776 const char *ctype;
777 locale_t loc;
779
781 {
782 HeapTuple tp;
783 Datum datum;
784
786 if (!HeapTupleIsValid(tp))
787 elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
790 collate = TextDatumGetCString(datum);
793 ctype = TextDatumGetCString(datum);
794
795 ReleaseSysCache(tp);
796 }
797 else
798 {
799 HeapTuple tp;
800 Datum datum;
801
803 if (!HeapTupleIsValid(tp))
804 elog(ERROR, "cache lookup failed for collation %u", collid);
805
808 collate = TextDatumGetCString(datum);
811 ctype = TextDatumGetCString(datum);
812
813 ReleaseSysCache(tp);
814 }
815
816
817 loc = make_libc_collator(collate, ctype);
818
819 result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
820 result->deterministic = true;
821 result->collate_is_c = (strcmp(collate, "C") == 0) ||
822 (strcmp(collate, "POSIX") == 0);
823 result->ctype_is_c = (strcmp(ctype, "C") == 0) ||
824 (strcmp(ctype, "POSIX") == 0);
825 result->lt = loc;
826 if (!result->collate_is_c)
827 {
828#ifdef WIN32
831 else
832#endif
833 result->collate = &collate_methods_libc;
834 }
835 if (!result->ctype_is_c)
836 {
839 else if (pg_database_encoding_max_length() > 1)
841 else
843 }
844
845 return result;
846}
847
848/*
849 * Create a locale_t with the given collation and ctype.
850 *
851 * The "C" and "POSIX" locales are not actually handled by libc, so return
852 * NULL.
853 *
854 * Ensure that no path leaks a locale_t.
855 */
856static locale_t
857make_libc_collator(const char *collate, const char *ctype)
858{
859 locale_t loc = 0;
860
861 if (strcmp(collate, ctype) == 0)
862 {
863 if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
864 {
865 /* Normal case where they're the same */
866 errno = 0;
867#ifndef WIN32
868 loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
869 NULL);
870#else
871 loc = _create_locale(LC_ALL, collate);
872#endif
873 if (!loc)
875 }
876 }
877 else
878 {
879#ifndef WIN32
880 /* We need two newlocale() steps */
881 locale_t loc1 = 0;
882
883 if (strcmp(collate, "C") != 0 && strcmp(collate, "POSIX") != 0)
884 {
885 errno = 0;
886 loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
887 if (!loc1)
889 }
890
891 if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
892 {
893 errno = 0;
894 loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
895 if (!loc)
896 {
897 if (loc1)
900 }
901 }
902 else
903 loc = loc1;
904#else
905
906 /*
907 * XXX The _create_locale() API doesn't appear to support this. Could
908 * perhaps be worked around by changing pg_locale_t to contain two
909 * separate fields.
910 */
913 errmsg("collations with different collate and ctype values are not supported on this platform")));
914#endif
915 }
916
917 return loc;
918}
919
920/*
921 * strncoll_libc
922 *
923 * NUL-terminate arguments and pass to strcoll_l().
924 */
925static int
926strncoll_libc(const char *arg1, size_t len1, const char *arg2, size_t len2,
927 pg_locale_t locale)
928{
929 char sbuf[TEXTBUFLEN];
930 char *buf = sbuf;
931 size_t bufsize1 = len1 + 1;
932 size_t bufsize2 = len2 + 1;
933 char *buf1;
934 char *buf2;
935 const char *arg1n;
936 const char *arg2n;
937 int result;
938
941
942 buf1 = buf;
943 buf2 = buf + bufsize1;
944
945 memcpy(buf1, arg1, len1);
946 buf1[len1] = '\0';
947 arg1n = buf1;
948
949 memcpy(buf2, arg2, len2);
950 buf2[len2] = '\0';
951 arg2n = buf2;
952
953 result = strcoll_l(arg1n, arg2n, locale->lt);
954
955 if (buf != sbuf)
956 pfree(buf);
957
958 return result;
959}
960
961/*
962 * strcoll_libc
963 */
964static int
965strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
966{
967 return strcoll_l(arg1, arg2, locale->lt);
968}
969
970/*
971 * strnxfrm_libc
972 *
973 * NUL-terminate src and pass to strxfrm_l().
974 */
975static size_t
976strnxfrm_libc(char *dest, size_t destsize, const char *src, size_t srclen,
977 pg_locale_t locale)
978{
979 char sbuf[TEXTBUFLEN];
980 char *buf = sbuf;
981 size_t bufsize = srclen + 1;
982 size_t result;
983
984 if (bufsize > TEXTBUFLEN)
985 buf = palloc(bufsize);
986
987 /* nul-terminate argument */
988 memcpy(buf, src, srclen);
989 buf[srclen] = '\0';
990
991 result = strxfrm_l(dest, buf, destsize, locale->lt);
992
993 if (buf != sbuf)
994 pfree(buf);
995
996 /* if dest is defined, it should be nul-terminated */
997 Assert(result >= destsize || dest[result] == '\0');
998
999 return result;
1000}
1001
1002/*
1003 * strxfrm_libc
1004 */
1005static size_t
1006strxfrm_libc(char *dest, size_t destsize, const char *src, pg_locale_t locale)
1007{
1008 return strxfrm_l(dest, src, destsize, locale->lt);
1009}
1010
1011char *
1013{
1014 char *collversion = NULL;
1015
1016 if (pg_strcasecmp("C", collcollate) != 0 &&
1017 pg_strncasecmp("C.", collcollate, 2) != 0 &&
1018 pg_strcasecmp("POSIX", collcollate) != 0)
1019 {
1020#if defined(__GLIBC__)
1021 /* Use the glibc version because we don't have anything better. */
1023#elif defined(LC_VERSION_MASK)
1024 locale_t loc;
1025
1026 /* Look up FreeBSD collation version. */
1028 if (loc)
1029 {
1030 collversion =
1032 freelocale(loc);
1033 }
1034 else
1035 ereport(ERROR,
1036 (errmsg("could not load locale \"%s\"", collcollate)));
1037#elif defined(WIN32)
1038 /*
1039 * If we are targeting Windows Vista and above, we can ask for a name
1040 * given a collation name (earlier versions required a location code
1041 * that we don't have).
1042 */
1043 NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
1045
1049 {
1050 /*
1051 * GetNLSVersionEx() wants a language tag such as "en-US", not a
1052 * locale name like "English_United States.1252". Until those
1053 * values can be prevented from entering the system, or 100%
1054 * reliably converted to the more useful tag format, tolerate the
1055 * resulting error and report that we have no version data.
1056 */
1058 return NULL;
1059
1060 ereport(ERROR,
1061 (errmsg("could not get collation version for locale \"%s\": error code %lu",
1063 GetLastError())));
1064 }
1065 collversion = psprintf("%lu.%lu,%lu.%lu",
1066 (version.dwNLSVersion >> 8) & 0xFFFF,
1067 version.dwNLSVersion & 0xFF,
1068 (version.dwDefinedVersion >> 8) & 0xFFFF,
1069 version.dwDefinedVersion & 0xFF);
1070#endif
1071 }
1072
1073 return collversion;
1074}
1075
1076/*
1077 * strncoll_libc_win32_utf8
1078 *
1079 * Win32 does not have UTF-8. Convert UTF8 arguments to wide characters and
1080 * invoke wcscoll_l().
1081 */
1082#ifdef WIN32
1083static int
1084strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2,
1085 size_t len2, pg_locale_t locale)
1086{
1087 char sbuf[TEXTBUFLEN];
1088 char *buf = sbuf;
1089 char *a1p,
1090 *a2p;
1091 size_t a1len,
1092 a2len,
1093 buflen;
1094 int r;
1095 int result;
1096
1098
1099 /*
1100 * In a 32-bit build, twice the input length can overflow size_t, so we
1101 * must be careful.
1102 */
1103 a1len = add_size(add_size(len1, len1), 2);
1104 a2len = add_size(add_size(len2, len2), 2);
1105 buflen = add_size(a1len, a2len);
1106
1107 if (buflen > TEXTBUFLEN)
1108 buf = palloc(buflen);
1109
1110 a1p = buf;
1111 a2p = buf + a1len;
1112
1113 /* API does not work for zero-length input */
1114 if (len1 == 0)
1115 r = 0;
1116 else
1117 {
1118 r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
1119 (LPWSTR) a1p, a1len / 2);
1120 if (!r)
1121 ereport(ERROR,
1122 (errmsg("could not convert string to UTF-16: error code %lu",
1123 GetLastError())));
1124 }
1125 ((LPWSTR) a1p)[r] = 0;
1126
1127 if (len2 == 0)
1128 r = 0;
1129 else
1130 {
1131 r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
1132 (LPWSTR) a2p, a2len / 2);
1133 if (!r)
1134 ereport(ERROR,
1135 (errmsg("could not convert string to UTF-16: error code %lu",
1136 GetLastError())));
1137 }
1138 ((LPWSTR) a2p)[r] = 0;
1139
1140 errno = 0;
1141 result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->lt);
1142 if (result == 2147483647) /* _NLSCMPERROR; missing from mingw headers */
1143 ereport(ERROR,
1144 (errmsg("could not compare Unicode strings: %m")));
1145
1146 if (buf != sbuf)
1147 pfree(buf);
1148
1149 return result;
1150}
1151
1152static int
1153strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
1154 pg_locale_t locale)
1155{
1156 size_t len1 = strlen(arg1);
1157 size_t len2 = strlen(arg2);
1158
1159 return strncoll_libc_win32_utf8(arg1, len1, arg2, len2, locale);
1160}
1161#endif /* WIN32 */
1162
1163/* simple subroutine for reporting errors from newlocale() */
1164void
1165report_newlocale_failure(const char *localename)
1166{
1167 int save_errno;
1168
1169 /*
1170 * Windows doesn't provide any useful error indication from
1171 * _create_locale(), and BSD-derived platforms don't seem to feel they
1172 * need to set errno either (even though POSIX is pretty clear that
1173 * newlocale should do so). So, if errno hasn't been set, assume ENOENT
1174 * is what to report.
1175 */
1176 if (errno == 0)
1177 errno = ENOENT;
1178
1179 /*
1180 * ENOENT means "no such locale", not "no such file", so clarify that
1181 * errno with an errdetail message.
1182 */
1183 save_errno = errno; /* auxiliary funcs might change errno */
1184 ereport(ERROR,
1186 errmsg("could not create locale \"%s\": %m",
1187 localename),
1188 (save_errno == ENOENT ?
1189 errdetail("The operating system could not find any locale data for the locale name \"%s\".",
1190 localename) : 0)));
1191}
1192
1193/*
1194 * POSIX doesn't define _l-variants of these functions, but several systems
1195 * have them. We provide our own replacements here.
1196 */
1197#ifndef HAVE_MBSTOWCS_L
1198static size_t
1199mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
1200{
1201#ifdef WIN32
1202 return _mbstowcs_l(dest, src, n, loc);
1203#else
1204 size_t result;
1206
1207 result = mbstowcs(dest, src, n);
1209 return result;
1210#endif
1211}
1212#endif
1213#ifndef HAVE_WCSTOMBS_L
1214static size_t
1215wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
1216{
1217#ifdef WIN32
1218 return _wcstombs_l(dest, src, n, loc);
1219#else
1220 size_t result;
1222
1223 result = wcstombs(dest, src, n);
1225 return result;
1226#endif
1227}
1228#endif
1229
1230/*
1231 * These functions convert from/to libc's wchar_t, *not* pg_wchar.
1232 * Therefore we keep them here rather than with the mbutils code.
1233 */
1234
1235/*
1236 * wchar2char --- convert wide characters to multibyte format
1237 *
1238 * This has the same API as the standard wcstombs_l() function; in particular,
1239 * tolen is the maximum number of bytes to store at *to, and *from must be
1240 * zero-terminated. The output will be zero-terminated iff there is room.
1241 */
1242size_t
1243wchar2char(char *to, const wchar_t *from, size_t tolen, locale_t loc)
1244{
1245 size_t result;
1246
1247 if (tolen == 0)
1248 return 0;
1249
1250#ifdef WIN32
1251
1252 /*
1253 * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding, and
1254 * for some reason mbstowcs and wcstombs won't do this for us, so we use
1255 * MultiByteToWideChar().
1256 */
1258 {
1259 result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
1260 NULL, NULL);
1261 /* A zero return is failure */
1262 if (result <= 0)
1263 result = -1;
1264 else
1265 {
1266 Assert(result <= tolen);
1267 /* Microsoft counts the zero terminator in the result */
1268 result--;
1269 }
1270 }
1271 else
1272#endif /* WIN32 */
1273 if (loc == (locale_t) 0)
1274 {
1275 /* Use wcstombs directly for the default locale */
1276 result = wcstombs(to, from, tolen);
1277 }
1278 else
1279 {
1280 /* Use wcstombs_l for nondefault locales */
1281 result = wcstombs_l(to, from, tolen, loc);
1282 }
1283
1284 return result;
1285}
1286
1287/*
1288 * char2wchar --- convert multibyte characters to wide characters
1289 *
1290 * This has almost the API of mbstowcs_l(), except that *from need not be
1291 * null-terminated; instead, the number of input bytes is specified as
1292 * fromlen. Also, we ereport() rather than returning -1 for invalid
1293 * input encoding. tolen is the maximum number of wchar_t's to store at *to.
1294 * The output will be zero-terminated iff there is room.
1295 */
1296static size_t
1297char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
1298 locale_t loc)
1299{
1300 size_t result;
1301
1302 if (tolen == 0)
1303 return 0;
1304
1305#ifdef WIN32
1306 /* See WIN32 "Unicode" comment above */
1308 {
1309 /* Win32 API does not work for zero-length input */
1310 if (fromlen == 0)
1311 result = 0;
1312 else
1313 {
1314 result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
1315 /* A zero return is failure */
1316 if (result == 0)
1317 result = -1;
1318 }
1319
1320 if (result != -1)
1321 {
1322 Assert(result < tolen);
1323 /* Append trailing null wchar (MultiByteToWideChar() does not) */
1324 to[result] = 0;
1325 }
1326 }
1327 else
1328#endif /* WIN32 */
1329 {
1330 /* mbstowcs requires ending '\0' */
1331 char *str = pnstrdup(from, fromlen);
1332
1333 if (loc == (locale_t) 0)
1334 {
1335 /* Use mbstowcs directly for the default locale */
1336 result = mbstowcs(to, str, tolen);
1337 }
1338 else
1339 {
1340 /* Use mbstowcs_l for nondefault locales */
1341 result = mbstowcs_l(to, str, tolen, loc);
1342 }
1343
1344 pfree(str);
1345 }
1346
1347 if (result == -1)
1348 {
1349 /*
1350 * Invalid multibyte character encountered. We try to give a useful
1351 * error message by letting pg_verifymbstr check the string. But it's
1352 * possible that the string is OK to us, and not OK to mbstowcs ---
1353 * this suggests that the LC_CTYPE locale is different from the
1354 * database encoding. Give a generic error message if pg_verifymbstr
1355 * can't find anything wrong.
1356 */
1357 pg_verifymbstr(from, fromlen, false); /* might not return */
1358 /* but if it does ... */
1359 ereport(ERROR,
1361 errmsg("invalid multibyte character for locale"),
1362 errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
1363 }
1364
1365 return result;
1366}
#define TextDatumGetCString(d)
Definition builtins.h:99
#define IS_HIGHBIT_SET(ch)
Definition c.h:1284
#define Assert(condition)
Definition c.h:1002
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Oid collid
int errcode(int sqlerrcode)
Definition elog.c:875
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_array(type, count)
Definition fe_memutils.h:91
Oid MyDatabaseId
Definition globals.c:96
const char * str
#define HeapTupleIsValid(tuple)
Definition htup.h:78
#define bufsize
int i
Definition isn.c:77
#define PG_UTF8
Definition mbprint.c:43
unsigned int pg_wchar
Definition mbprint.c:31
int GetDatabaseEncoding(void)
Definition mbutils.c:1389
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
Definition mbutils.c:1683
int pg_database_encoding_max_length(void)
Definition mbutils.c:1673
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
Size add_size(Size s1, Size s2)
Definition mcxt.c:1733
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc(Size size)
Definition mcxt.c:1390
char * pnstrdup(const char *in, Size len)
Definition mcxt.c:1921
static char * errmsg
static size_t strupper_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static bool wc_isalpha_libc_mb(pg_wchar wc, pg_locale_t locale)
static bool wc_ispunct_libc_sb(pg_wchar wc, pg_locale_t locale)
static const struct ctype_methods ctype_methods_libc_other_mb
static const struct ctype_methods ctype_methods_libc_utf8
static pg_wchar toupper_libc_mb(pg_wchar wc, pg_locale_t locale)
static bool wc_iscased_libc_sb(pg_wchar wc, pg_locale_t locale)
static bool wc_isprint_libc_mb(pg_wchar wc, pg_locale_t locale)
static bool wc_isdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context)
size_t wchar2char(char *to, const wchar_t *from, size_t tolen, locale_t loc)
static bool wc_isspace_libc_sb(pg_wchar wc, pg_locale_t locale)
static int strncoll_libc(const char *arg1, size_t len1, const char *arg2, size_t len2, pg_locale_t locale)
static bool wc_islower_libc_sb(pg_wchar wc, pg_locale_t locale)
static bool wc_isupper_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t strtitle_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static pg_wchar toupper_libc_sb(pg_wchar wc, pg_locale_t locale)
static bool wc_isgraph_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, locale_t loc)
static bool wc_isalnum_libc_mb(pg_wchar wc, pg_locale_t locale)
static bool wc_isalnum_libc_sb(pg_wchar wc, pg_locale_t locale)
static size_t strtitle_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static bool wc_isalpha_libc_sb(pg_wchar wc, pg_locale_t locale)
static bool wc_isprint_libc_sb(pg_wchar wc, pg_locale_t locale)
char * get_collation_actual_version_libc(const char *collcollate)
static bool wc_isupper_libc_sb(pg_wchar wc, pg_locale_t locale)
static locale_t make_libc_collator(const char *collate, const char *ctype)
static bool wc_isgraph_libc_sb(pg_wchar wc, pg_locale_t locale)
static bool wc_iscased_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t strxfrm_libc(char *dest, size_t destsize, const char *src, pg_locale_t locale)
static pg_wchar tolower_libc_sb(pg_wchar wc, pg_locale_t locale)
static size_t wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
static const struct collate_methods collate_methods_libc
static bool wc_ispunct_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t strnxfrm_libc(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static bool wc_islower_libc_mb(pg_wchar wc, pg_locale_t locale)
static const struct ctype_methods ctype_methods_libc_sb
static bool wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
void report_newlocale_failure(const char *localename)
static pg_wchar tolower_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t downcase_ident_libc_sb(char *dst, size_t dstsize, const char *src, size_t srclen, pg_locale_t locale)
static bool wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
static bool wc_isspace_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t strlower_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static size_t strupper_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
#define TEXTBUFLEN
static int strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
static bool wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
static size_t mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
static size_t strlower_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
static char buf[DEFAULT_XLOG_SEG_SIZE]
int pg_strcasecmp(const char *s1, const char *s2)
static unsigned char pg_ascii_tolower(unsigned char ch)
Definition port.h:189
static unsigned char pg_ascii_toupper(unsigned char ch)
Definition port.h:178
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
unsigned int Oid
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
int(* strncoll)(const char *arg1, size_t len1, const char *arg2, size_t len2, pg_locale_t locale)
Definition pg_locale.h:66
size_t(* strlower)(char *dest, size_t destsize, const char *src, size_t srclen, pg_locale_t locale)
Definition pg_locale.h:101
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:626
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
#define locale_t
Definition win32_port.h:446
#define toupper_l
Definition win32_port.h:448
#define iswalnum_l
Definition win32_port.h:456
#define isgraph_l
Definition win32_port.h:461
#define towupper_l
Definition win32_port.h:450
#define ispunct_l
Definition win32_port.h:465
#define isalpha_l
Definition win32_port.h:453
#define strcoll_l
Definition win32_port.h:469
#define iswgraph_l
Definition win32_port.h:462
#define strxfrm_l
Definition win32_port.h:470
#define towlower_l
Definition win32_port.h:449
#define iswspace_l
Definition win32_port.h:468
#define isdigit_l
Definition win32_port.h:451
#define wcscoll_l
Definition win32_port.h:471
#define tolower_l
Definition win32_port.h:447
#define iswupper_l
Definition win32_port.h:458
#define iswalpha_l
Definition win32_port.h:454
#define isprint_l
Definition win32_port.h:463
#define iswprint_l
Definition win32_port.h:464
#define isupper_l
Definition win32_port.h:457
#define isalnum_l
Definition win32_port.h:455
#define islower_l
Definition win32_port.h:459
#define iswlower_l
Definition win32_port.h:460
#define iswpunct_l
Definition win32_port.h:466
#define isspace_l
Definition win32_port.h:467
#define iswdigit_l
Definition win32_port.h:452