@@ -499,6 +499,12 @@ pg_collation_actual_version(PG_FUNCTION_ARGS)
499
499
#define READ_LOCALE_A_OUTPUT
500
500
#endif
501
501
502
+ /* will we use EnumSystemLocalesEx in pg_import_system_collations? */
503
+ #ifdef WIN32
504
+ #define ENUM_SYSTEM_LOCALE
505
+ #endif
506
+
507
+
502
508
#ifdef READ_LOCALE_A_OUTPUT
503
509
/*
504
510
* "Normalize" a libc locale name, stripping off encoding tags such as
@@ -610,6 +616,161 @@ get_icu_locale_comment(const char *localename)
610
616
#endif /* USE_ICU */
611
617
612
618
619
+ /*
620
+ * Create a new collation using the input locale 'locale'. (subroutine for
621
+ * pg_import_system_collations())
622
+ *
623
+ * 'nspid' is the namespace id where the collation will be created.
624
+ *
625
+ * 'nvalidp' is incremented if the locale has a valid encoding.
626
+ *
627
+ * 'ncreatedp' is incremented if the collation is actually created. If the
628
+ * collation already exists it will quietly do nothing.
629
+ *
630
+ * The returned value is the encoding of the locale, -1 if the locale is not
631
+ * valid for creating a collation.
632
+ *
633
+ */
634
+ pg_attribute_unused ()
635
+ static int
636
+ create_collation_from_locale (const char * locale , int nspid ,
637
+ int * nvalidp , int * ncreatedp )
638
+ {
639
+ int enc ;
640
+ Oid collid ;
641
+
642
+ /*
643
+ * Some systems have locale names that don't consist entirely of
644
+ * ASCII letters (such as "bokmål" or "français").
645
+ * This is pretty silly, since we need the locale itself to
646
+ * interpret the non-ASCII characters. We can't do much with
647
+ * those, so we filter them out.
648
+ */
649
+ if (!pg_is_ascii (locale ))
650
+ {
651
+ elog (DEBUG1 , "skipping locale with non-ASCII name: \"%s\"" , locale );
652
+ return -1 ;
653
+ }
654
+
655
+ enc = pg_get_encoding_from_locale (locale , false);
656
+ if (enc < 0 )
657
+ {
658
+ elog (DEBUG1 , "skipping locale with unrecognized encoding: \"%s\"" , locale );
659
+ return -1 ;
660
+ }
661
+ if (!PG_VALID_BE_ENCODING (enc ))
662
+ {
663
+ elog (DEBUG1 , "skipping locale with client-only encoding: \"%s\"" , locale );
664
+ return -1 ;
665
+ }
666
+ if (enc == PG_SQL_ASCII )
667
+ return -1 ; /* C/POSIX are already in the catalog */
668
+
669
+ /* count valid locales found in operating system */
670
+ (* nvalidp )++ ;
671
+
672
+ /*
673
+ * Create a collation named the same as the locale, but quietly
674
+ * doing nothing if it already exists. This is the behavior we
675
+ * need even at initdb time, because some versions of "locale -a"
676
+ * can report the same locale name more than once. And it's
677
+ * convenient for later import runs, too, since you just about
678
+ * always want to add on new locales without a lot of chatter
679
+ * about existing ones.
680
+ */
681
+ collid = CollationCreate (locale , nspid , GetUserId (),
682
+ COLLPROVIDER_LIBC , true, enc ,
683
+ locale , locale , NULL ,
684
+ get_collation_actual_version (COLLPROVIDER_LIBC , locale ),
685
+ true, true);
686
+ if (OidIsValid (collid ))
687
+ {
688
+ (* ncreatedp )++ ;
689
+
690
+ /* Must do CCI between inserts to handle duplicates correctly */
691
+ CommandCounterIncrement ();
692
+ }
693
+
694
+ return enc ;
695
+ }
696
+
697
+
698
+ #ifdef ENUM_SYSTEM_LOCALE
699
+ /* parameter to be passed to the callback function win32_read_locale() */
700
+ typedef struct
701
+ {
702
+ Oid nspid ;
703
+ int * ncreatedp ;
704
+ int * nvalidp ;
705
+ } CollParam ;
706
+
707
+ /*
708
+ * Callback function for EnumSystemLocalesEx() in
709
+ * pg_import_system_collations(). Creates a collation for every valid locale
710
+ * and a POSIX alias collation.
711
+ *
712
+ * The callback contract is to return TRUE to continue enumerating and FALSE
713
+ * to stop enumerating. We always want to continue.
714
+ */
715
+ static BOOL CALLBACK
716
+ win32_read_locale (LPWSTR pStr , DWORD dwFlags , LPARAM lparam )
717
+ {
718
+ CollParam * param = (CollParam * ) lparam ;
719
+ char localebuf [NAMEDATALEN ];
720
+ int result ;
721
+ int enc ;
722
+
723
+ (void ) dwFlags ;
724
+
725
+ result = WideCharToMultiByte (CP_ACP , 0 , pStr , -1 , localebuf , NAMEDATALEN ,
726
+ NULL , NULL );
727
+
728
+ if (result == 0 )
729
+ {
730
+ if (GetLastError () == ERROR_INSUFFICIENT_BUFFER )
731
+ elog (DEBUG1 , "skipping locale with too-long name: \"%s\"" , localebuf );
732
+ return TRUE;
733
+ }
734
+ if (localebuf [0 ] == '\0' )
735
+ return TRUE;
736
+
737
+ enc = create_collation_from_locale (localebuf , param -> nspid ,
738
+ param -> nvalidp , param -> ncreatedp );
739
+ if (enc < 0 )
740
+ return TRUE;
741
+
742
+ /*
743
+ * Windows will use hyphens between language and territory, where POSIX
744
+ * uses an underscore. Simply create a POSIX alias.
745
+ */
746
+ if (strchr (localebuf , '-' ))
747
+ {
748
+ char alias [NAMEDATALEN ];
749
+ Oid collid ;
750
+
751
+ strcpy (alias , localebuf );
752
+ for (char * p = alias ; * p ; p ++ )
753
+ if (* p == '-' )
754
+ * p = '_' ;
755
+
756
+ collid = CollationCreate (alias , param -> nspid , GetUserId (),
757
+ COLLPROVIDER_LIBC , true, enc ,
758
+ localebuf , localebuf , NULL ,
759
+ get_collation_actual_version (COLLPROVIDER_LIBC , localebuf ),
760
+ true, true);
761
+ if (OidIsValid (collid ))
762
+ {
763
+ (* param -> ncreatedp )++ ;
764
+
765
+ CommandCounterIncrement ();
766
+ }
767
+ }
768
+
769
+ return TRUE;
770
+ }
771
+ #endif /* ENUM_SYSTEM_LOCALE */
772
+
773
+
613
774
/*
614
775
* pg_import_system_collations: add known system collations to pg_collation
615
776
*/
@@ -668,58 +829,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
668
829
}
669
830
localebuf [len - 1 ] = '\0' ;
670
831
671
- /*
672
- * Some systems have locale names that don't consist entirely of
673
- * ASCII letters (such as "bokmål" or "français").
674
- * This is pretty silly, since we need the locale itself to
675
- * interpret the non-ASCII characters. We can't do much with
676
- * those, so we filter them out.
677
- */
678
- if (!pg_is_ascii (localebuf ))
679
- {
680
- elog (DEBUG1 , "skipping locale with non-ASCII name: \"%s\"" , localebuf );
681
- continue ;
682
- }
683
-
684
- enc = pg_get_encoding_from_locale (localebuf , false);
832
+ enc = create_collation_from_locale (localebuf , nspid , & nvalid , & ncreated );
685
833
if (enc < 0 )
686
- {
687
- elog (DEBUG1 , "skipping locale with unrecognized encoding: \"%s\"" ,
688
- localebuf );
689
- continue ;
690
- }
691
- if (!PG_VALID_BE_ENCODING (enc ))
692
- {
693
- elog (DEBUG1 , "skipping locale with client-only encoding: \"%s\"" , localebuf );
694
834
continue ;
695
- }
696
- if (enc == PG_SQL_ASCII )
697
- continue ; /* C/POSIX are already in the catalog */
698
-
699
- /* count valid locales found in operating system */
700
- nvalid ++ ;
701
-
702
- /*
703
- * Create a collation named the same as the locale, but quietly
704
- * doing nothing if it already exists. This is the behavior we
705
- * need even at initdb time, because some versions of "locale -a"
706
- * can report the same locale name more than once. And it's
707
- * convenient for later import runs, too, since you just about
708
- * always want to add on new locales without a lot of chatter
709
- * about existing ones.
710
- */
711
- collid = CollationCreate (localebuf , nspid , GetUserId (),
712
- COLLPROVIDER_LIBC , true, enc ,
713
- localebuf , localebuf , NULL ,
714
- get_collation_actual_version (COLLPROVIDER_LIBC , localebuf ),
715
- true, true);
716
- if (OidIsValid (collid ))
717
- {
718
- ncreated ++ ;
719
-
720
- /* Must do CCI between inserts to handle duplicates correctly */
721
- CommandCounterIncrement ();
722
- }
723
835
724
836
/*
725
837
* Generate aliases such as "en_US" in addition to "en_US.utf8"
@@ -857,5 +969,30 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
857
969
}
858
970
#endif /* USE_ICU */
859
971
972
+ /* Load collations known to WIN32 */
973
+ #ifdef ENUM_SYSTEM_LOCALE
974
+ {
975
+ int nvalid = 0 ;
976
+ CollParam param ;
977
+
978
+ param .nspid = nspid ;
979
+ param .ncreatedp = & ncreated ;
980
+ param .nvalidp = & nvalid ;
981
+
982
+ /*
983
+ * Enumerate the locales that are either installed on or supported
984
+ * by the OS.
985
+ */
986
+ if (!EnumSystemLocalesEx (win32_read_locale , LOCALE_ALL ,
987
+ (LPARAM ) & param , NULL ))
988
+ _dosmaperr (GetLastError ());
989
+
990
+ /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */
991
+ if (nvalid == 0 )
992
+ ereport (WARNING ,
993
+ (errmsg ("no usable system locales were found" )));
994
+ }
995
+ #endif /* ENUM_SYSTEM_LOCALE */
996
+
860
997
PG_RETURN_INT32 (ncreated );
861
998
}
0 commit comments