## Header files
 ##
 
-for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h mbarrier.h sys/epoll.h sys/event.h sys/personality.h sys/prctl.h sys/procctl.h sys/signalfd.h sys/ucred.h termios.h ucred.h xlocale.h
+for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h mbarrier.h sys/epoll.h sys/event.h sys/personality.h sys/prctl.h sys/procctl.h sys/signalfd.h sys/ucred.h termios.h uchar.h ucred.h xlocale.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
 
    sys/signalfd.h
    sys/ucred.h
    termios.h
+   uchar.h
    ucred.h
    xlocale.h
 ]))
 
   'sys/signalfd.h',
   'sys/ucred.h',
   'termios.h',
+  'uchar.h',
   'ucred.h',
   'xlocale.h',
 ]
 
 
 /* is Unicode code point acceptable? */
 static void
-check_unicode_value(pg_wchar c)
+check_unicode_value(char32_t c)
 {
    if (!is_valid_unicode_codepoint(c))
        ereport(ERROR,
    char       *new,
               *out;
    size_t      new_len;
-   pg_wchar    pair_first = 0;
+   char16_t    pair_first = 0;
    ScannerCallbackState scbstate;
 
    /*
                     isxdigit((unsigned char) in[3]) &&
                     isxdigit((unsigned char) in[4]))
            {
-               pg_wchar    unicode;
+               char32_t    unicode;
 
                unicode = (hexval(in[1]) << 12) +
                    (hexval(in[2]) << 8) +
                     isxdigit((unsigned char) in[6]) &&
                     isxdigit((unsigned char) in[7]))
            {
-               pg_wchar    unicode;
+               char32_t    unicode;
 
                unicode = (hexval(in[2]) << 20) +
                    (hexval(in[3]) << 16) +
 
 static char *litbufdup(core_yyscan_t yyscanner);
 static unsigned char unescape_single_char(unsigned char c, core_yyscan_t yyscanner);
 static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
-static void addunicode(pg_wchar c, yyscan_t yyscanner);
+static void addunicode(char32_t c, yyscan_t yyscanner);
 
 #define yyerror(msg)  scanner_yyerror(msg, yyscanner)
 
                    addlit(yytext, yyleng, yyscanner);
                }
 <xe>{xeunicode} {
-                   pg_wchar    c = strtoul(yytext + 2, NULL, 16);
+                   char32_t    c = strtoul(yytext + 2, NULL, 16);
 
                    /*
                     * For consistency with other productions, issue any
                    POP_YYLLOC();
                }
 <xeu>{xeunicode} {
-                   pg_wchar    c = strtoul(yytext + 2, NULL, 16);
+                   char32_t    c = strtoul(yytext + 2, NULL, 16);
 
                    /* Remember start of overall string token ... */
                    PUSH_YYLLOC();
 }
 
 static void
-addunicode(pg_wchar c, core_yyscan_t yyscanner)
+addunicode(char32_t c, core_yyscan_t yyscanner)
 {
    ScannerCallbackState scbstate;
    char        buf[MAX_UNICODE_EQUIVALENT_STRING + 1];
 
 
 /* Add given unicode character to scanstring */
 static bool
-addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
+addUnicodeChar(char32_t ch, struct Node *escontext, yyscan_t yyscanner)
 {
    if (ch == 0)
    {
 
 /* Add unicode character, processing any surrogate pairs */
 static bool
-addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
+addUnicode(char32_t ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
 {
    if (is_utf16_surrogate_first(ch))
    {
 
    for (i = 2; i < l; i += 2)  /* skip '\u' */
    {
-       int         ch = 0;
+       char32_t        ch = 0;
        int         j,
                    si;
 
 
 #include "catalog/pg_collation.h"
 #include "common/unicode_case.h"
 #include "common/unicode_category.h"
-#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
 #include "utils/pg_locale.h"
    bool        prev_alnum;
 };
 
+/*
+ * In UTF-8, pg_wchar is guaranteed to be the code point value.
+ */
+static inline char32_t
+to_char32(pg_wchar wc)
+{
+   Assert(GetDatabaseEncoding() == PG_UTF8);
+   return (char32_t) wc;
+}
+
+static inline pg_wchar
+to_pg_wchar(char32_t c32)
+{
+   Assert(GetDatabaseEncoding() == PG_UTF8);
+   return (pg_wchar) c32;
+}
+
 /*
  * Simple word boundary iterator that draws boundaries each time the result of
  * pg_u_isalnum() changes.
    while (wbstate->offset < wbstate->len &&
           wbstate->str[wbstate->offset] != '\0')
    {
-       pg_wchar    u = utf8_to_unicode((unsigned char *) wbstate->str +
+       char32_t    u = utf8_to_unicode((unsigned char *) wbstate->str +
                                        wbstate->offset);
        bool        curr_alnum = pg_u_isalnum(u, wbstate->posix);
 
 static bool
 wc_isdigit_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isdigit(wc, !locale->builtin.casemap_full);
+   return pg_u_isdigit(to_char32(wc), !locale->builtin.casemap_full);
 }
 
 static bool
 wc_isalpha_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isalpha(wc);
+   return pg_u_isalpha(to_char32(wc));
 }
 
 static bool
 wc_isalnum_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isalnum(wc, !locale->builtin.casemap_full);
+   return pg_u_isalnum(to_char32(wc), !locale->builtin.casemap_full);
 }
 
 static bool
 wc_isupper_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isupper(wc);
+   return pg_u_isupper(to_char32(wc));
 }
 
 static bool
 wc_islower_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_islower(wc);
+   return pg_u_islower(to_char32(wc));
 }
 
 static bool
 wc_isgraph_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isgraph(wc);
+   return pg_u_isgraph(to_char32(wc));
 }
 
 static bool
 wc_isprint_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isprint(wc);
+   return pg_u_isprint(to_char32(wc));
 }
 
 static bool
 wc_ispunct_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_ispunct(wc, !locale->builtin.casemap_full);
+   return pg_u_ispunct(to_char32(wc), !locale->builtin.casemap_full);
 }
 
 static bool
 wc_isspace_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isspace(wc);
+   return pg_u_isspace(to_char32(wc));
 }
 
 static bool
 wc_isxdigit_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return pg_u_isxdigit(wc, !locale->builtin.casemap_full);
+   return pg_u_isxdigit(to_char32(wc), !locale->builtin.casemap_full);
 }
 
 static bool
 static pg_wchar
 wc_toupper_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return unicode_uppercase_simple(wc);
+   return to_pg_wchar(unicode_uppercase_simple(to_char32(wc)));
 }
 
 static pg_wchar
 wc_tolower_builtin(pg_wchar wc, pg_locale_t locale)
 {
-   return unicode_lowercase_simple(wc);
+   return to_pg_wchar(unicode_lowercase_simple(to_char32(wc)));
 }
 
 static const struct ctype_methods ctype_methods_builtin = {
 
        ereport(ERROR,
                (errmsg("Unicode categorization can only be performed if server encoding is UTF8")));
 
-   /* convert to pg_wchar */
+   /* convert to char32_t */
    size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
    p = (unsigned char *) VARDATA_ANY(input);
    for (int i = 0; i < size; i++)
    {
-       pg_wchar    uchar = utf8_to_unicode(p);
+       char32_t    uchar = utf8_to_unicode(p);
        int         category = unicode_category(uchar);
 
        if (category == PG_U_UNASSIGNED)
    char       *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
    UnicodeNormalizationForm form;
    int         size;
-   pg_wchar   *input_chars;
-   pg_wchar   *output_chars;
+   char32_t   *input_chars;
+   char32_t   *output_chars;
    unsigned char *p;
    text       *result;
    int         i;
 
    form = unicode_norm_form_from_string(formstr);
 
-   /* convert to pg_wchar */
+   /* convert to char32_t */
    size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
-   input_chars = palloc((size + 1) * sizeof(pg_wchar));
+   input_chars = palloc((size + 1) * sizeof(char32_t));
    p = (unsigned char *) VARDATA_ANY(input);
    for (i = 0; i < size; i++)
    {
        input_chars[i] = utf8_to_unicode(p);
        p += pg_utf_mblen(p);
    }
-   input_chars[i] = (pg_wchar) '\0';
+   input_chars[i] = (char32_t) '\0';
    Assert((char *) p == VARDATA_ANY(input) + VARSIZE_ANY_EXHDR(input));
 
    /* action */
 
    /* convert back to UTF-8 string */
    size = 0;
-   for (pg_wchar *wp = output_chars; *wp; wp++)
+   for (char32_t *wp = output_chars; *wp; wp++)
    {
        unsigned char buf[4];
 
    SET_VARSIZE(result, size + VARHDRSZ);
 
    p = (unsigned char *) VARDATA_ANY(result);
-   for (pg_wchar *wp = output_chars; *wp; wp++)
+   for (char32_t *wp = output_chars; *wp; wp++)
    {
        unicode_to_utf8(*wp, p);
        p += pg_utf_mblen(p);
    char       *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
    UnicodeNormalizationForm form;
    int         size;
-   pg_wchar   *input_chars;
-   pg_wchar   *output_chars;
+   char32_t   *input_chars;
+   char32_t   *output_chars;
    unsigned char *p;
    int         i;
    UnicodeNormalizationQC quickcheck;
 
    form = unicode_norm_form_from_string(formstr);
 
-   /* convert to pg_wchar */
+   /* convert to char32_t */
    size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
-   input_chars = palloc((size + 1) * sizeof(pg_wchar));
+   input_chars = palloc((size + 1) * sizeof(char32_t));
    p = (unsigned char *) VARDATA_ANY(input);
    for (i = 0; i < size; i++)
    {
        input_chars[i] = utf8_to_unicode(p);
        p += pg_utf_mblen(p);
    }
-   input_chars[i] = (pg_wchar) '\0';
+   input_chars[i] = (char32_t) '\0';
    Assert((char *) p == VARDATA_ANY(input) + VARSIZE_ANY_EXHDR(input));
 
    /* quick check (see UAX #15) */
    output_chars = unicode_normalize(form, input_chars);
 
    output_size = 0;
-   for (pg_wchar *wp = output_chars; *wp; wp++)
+   for (char32_t *wp = output_chars; *wp; wp++)
        output_size++;
 
    result = (size == output_size) &&
-       (memcmp(input_chars, output_chars, size * sizeof(pg_wchar)) == 0);
+       (memcmp(input_chars, output_chars, size * sizeof(char32_t)) == 0);
 
    PG_RETURN_BOOL(result);
 }
    int         len;
    StringInfoData str;
    text       *result;
-   pg_wchar    pair_first = 0;
+   char16_t    pair_first = 0;
    char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
 
    instr = VARDATA_ANY(input_text);
            else if ((len >= 5 && isxdigits_n(instr + 1, 4)) ||
                     (len >= 6 && instr[1] == 'u' && isxdigits_n(instr + 2, 4)))
            {
-               pg_wchar    unicode;
+               char32_t    unicode;
                int         offset = instr[1] == 'u' ? 2 : 1;
 
                unicode = hexval_n(instr + offset, 4);
            }
            else if (len >= 8 && instr[1] == '+' && isxdigits_n(instr + 2, 6))
            {
-               pg_wchar    unicode;
+               char32_t    unicode;
 
                unicode = hexval_n(instr + 2, 6);
 
            }
            else if (len >= 10 && instr[1] == 'U' && isxdigits_n(instr + 2, 8))
            {
-               pg_wchar    unicode;
+               char32_t    unicode;
 
                unicode = hexval_n(instr + 2, 8);
 
 
  * may call this outside any transaction, or in an aborted transaction.
  */
 void
-pg_unicode_to_server(pg_wchar c, unsigned char *s)
+pg_unicode_to_server(char32_t c, unsigned char *s)
 {
    unsigned char c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1];
    int         c_as_utf8_len;
  * but simply return false on conversion failure.
  */
 bool
-pg_unicode_to_server_noerror(pg_wchar c, unsigned char *s)
+pg_unicode_to_server_noerror(char32_t c, unsigned char *s)
 {
    unsigned char c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1];
    int         c_as_utf8_len;
 
 
 /* Prototypes for local functions */
 static int codepoint_range_cmp(const void *a, const void *b);
-static bool is_code_in_table(pg_wchar code, const pg_wchar *map, int mapsize);
+static bool is_code_in_table(char32_t code, const char32_t *map, int mapsize);
 static int pg_utf8_string_len(const char *source);
 
 /*
  *
  * These are all mapped to the ASCII space character (U+00A0).
  */
-static const pg_wchar non_ascii_space_ranges[] =
+static const char32_t non_ascii_space_ranges[] =
 {
    0x00A0, 0x00A0,
    0x1680, 0x1680,
  *
  * If any of these appear in the input, they are removed.
  */
-static const pg_wchar commonly_mapped_to_nothing_ranges[] =
+static const char32_t commonly_mapped_to_nothing_ranges[] =
 {
    0x00AD, 0x00AD,
    0x034F, 0x034F,
  * tables, so one code might originate from multiple source tables.
  * Adjacent ranges have also been merged together, to save space.
  */
-static const pg_wchar prohibited_output_ranges[] =
+static const char32_t prohibited_output_ranges[] =
 {
    0x0000, 0x001F,             /* C.2.1 */
    0x007F, 0x00A0,             /* C.1.2, C.2.1, C.2.2 */
 };
 
 /* A.1 Unassigned code points in Unicode 3.2 */
-static const pg_wchar unassigned_codepoint_ranges[] =
+static const char32_t unassigned_codepoint_ranges[] =
 {
    0x0221, 0x0221,
    0x0234, 0x024F,
 };
 
 /* D.1 Characters with bidirectional property "R" or "AL" */
-static const pg_wchar RandALCat_codepoint_ranges[] =
+static const char32_t RandALCat_codepoint_ranges[] =
 {
    0x05BE, 0x05BE,
    0x05C0, 0x05C0,
 };
 
 /* D.2 Characters with bidirectional property "L" */
-static const pg_wchar LCat_codepoint_ranges[] =
+static const char32_t LCat_codepoint_ranges[] =
 {
    0x0041, 0x005A,
    0x0061, 0x007A,
 static int
 codepoint_range_cmp(const void *a, const void *b)
 {
-   const pg_wchar *key = (const pg_wchar *) a;
-   const pg_wchar *range = (const pg_wchar *) b;
+   const char32_t *key = (const char32_t *) a;
+   const char32_t *range = (const char32_t *) b;
 
    if (*key < range[0])
        return -1;              /* less than lower bound */
 }
 
 static bool
-is_code_in_table(pg_wchar code, const pg_wchar *map, int mapsize)
+is_code_in_table(char32_t code, const char32_t *map, int mapsize)
 {
    Assert(mapsize % 2 == 0);
 
    if (code < map[0] || code > map[mapsize - 1])
        return false;
 
-   if (bsearch(&code, map, mapsize / 2, sizeof(pg_wchar) * 2,
+   if (bsearch(&code, map, mapsize / 2, sizeof(char32_t) * 2,
                codepoint_range_cmp))
        return true;
    else
 pg_saslprep_rc
 pg_saslprep(const char *input, char **output)
 {
-   pg_wchar   *input_chars = NULL;
-   pg_wchar   *output_chars = NULL;
+   char32_t   *input_chars = NULL;
+   char32_t   *output_chars = NULL;
    int         input_size;
    char       *result;
    int         result_size;
    int         i;
    bool        contains_RandALCat;
    unsigned char *p;
-   pg_wchar   *wp;
+   char32_t   *wp;
 
    /* Ensure we return *output as NULL on failure */
    *output = NULL;
    input_size = pg_utf8_string_len(input);
    if (input_size < 0)
        return SASLPREP_INVALID_UTF8;
-   if (input_size >= MaxAllocSize / sizeof(pg_wchar))
+   if (input_size >= MaxAllocSize / sizeof(char32_t))
        goto oom;
 
-   input_chars = ALLOC((input_size + 1) * sizeof(pg_wchar));
+   input_chars = ALLOC((input_size + 1) * sizeof(char32_t));
    if (!input_chars)
        goto oom;
 
        input_chars[i] = utf8_to_unicode(p);
        p += pg_utf_mblen(p);
    }
-   input_chars[i] = (pg_wchar) '\0';
+   input_chars[i] = (char32_t) '\0';
 
    /*
     * The steps below correspond to the steps listed in [RFC3454], Section
    count = 0;
    for (i = 0; i < input_size; i++)
    {
-       pg_wchar    code = input_chars[i];
+       char32_t    code = input_chars[i];
 
        if (IS_CODE_IN_TABLE(code, non_ascii_space_ranges))
            input_chars[count++] = 0x0020;
        else
            input_chars[count++] = code;
    }
-   input_chars[count] = (pg_wchar) '\0';
+   input_chars[count] = (char32_t) '\0';
    input_size = count;
 
    if (input_size == 0)
     */
    for (i = 0; i < input_size; i++)
    {
-       pg_wchar    code = input_chars[i];
+       char32_t    code = input_chars[i];
 
        if (IS_CODE_IN_TABLE(code, prohibited_output_ranges))
            goto prohibited;
    contains_RandALCat = false;
    for (i = 0; i < input_size; i++)
    {
-       pg_wchar    code = input_chars[i];
+       char32_t    code = input_chars[i];
 
        if (IS_CODE_IN_TABLE(code, RandALCat_codepoint_ranges))
        {
 
    if (contains_RandALCat)
    {
-       pg_wchar    first = input_chars[0];
-       pg_wchar    last = input_chars[input_size - 1];
+       char32_t    first = input_chars[0];
+       char32_t    last = input_chars[input_size - 1];
 
        for (i = 0; i < input_size; i++)
        {
-           pg_wchar    code = input_chars[i];
+           char32_t    code = input_chars[i];
 
            if (IS_CODE_IN_TABLE(code, LCat_codepoint_ranges))
                goto prohibited;
 
 #include "common/unicode_case.h"
 #include "common/unicode_category.h"
 #include "common/unicode_version.h"
+#include "mb/pg_wchar.h"
 
 /* enough to hold largest source or result string, including NUL */
 #define BUFSZ 256
    while (wbstate->offset < wbstate->len &&
           wbstate->str[wbstate->offset] != '\0')
    {
-       pg_wchar    u = utf8_to_unicode((unsigned char *) wbstate->str +
+       char32_t    u = utf8_to_unicode((unsigned char *) wbstate->str +
                                        wbstate->offset);
        bool        curr_alnum = pg_u_isalnum(u, wbstate->posix);
 
 #ifdef USE_ICU
 
 static void
-icu_test_simple(pg_wchar code)
+icu_test_simple(char32_t code)
 {
-   pg_wchar    lower = unicode_lowercase_simple(code);
-   pg_wchar    title = unicode_titlecase_simple(code);
-   pg_wchar    upper = unicode_uppercase_simple(code);
-   pg_wchar    fold = unicode_casefold_simple(code);
-   pg_wchar    iculower = u_tolower(code);
-   pg_wchar    icutitle = u_totitle(code);
-   pg_wchar    icuupper = u_toupper(code);
-   pg_wchar    icufold = u_foldCase(code, U_FOLD_CASE_DEFAULT);
+   char32_t    lower = unicode_lowercase_simple(code);
+   char32_t    title = unicode_titlecase_simple(code);
+   char32_t    upper = unicode_uppercase_simple(code);
+   char32_t    fold = unicode_casefold_simple(code);
+   char32_t    iculower = u_tolower(code);
+   char32_t    icutitle = u_totitle(code);
+   char32_t    icuupper = u_toupper(code);
+   char32_t    icufold = u_foldCase(code, U_FOLD_CASE_DEFAULT);
 
    if (lower != iculower || title != icutitle || upper != icuupper ||
        fold != icufold)
    int         successful = 0;
    int         skipped_mismatch = 0;
 
-   for (pg_wchar code = 0; code <= 0x10ffff; code++)
+   for (char32_t code = 0; code <= 0x10ffff; code++)
    {
        pg_unicode_category category = unicode_category(code);
 
 
 
 #include "common/unicode_category.h"
 #include "common/unicode_version.h"
+#include "mb/pg_wchar.h"
 
 static int pg_unicode_version = 0;
 #ifdef USE_ICU
    int         pg_skipped_codepoints = 0;
    int         icu_skipped_codepoints = 0;
 
-   for (pg_wchar code = 0; code <= 0x10ffff; code++)
+   for (char32_t code = 0; code <= 0x10ffff; code++)
    {
        uint8_t     pg_category = unicode_category(code);
        uint8_t     icu_category = u_charType(code);
 
 typedef struct
 {
    int         linenum;
-   pg_wchar    input[50];
-   pg_wchar    output[4][50];
+   char32_t    input[50];
+   char32_t    output[4][50];
 } pg_unicode_test;
 
 /* test table */
 
  */
 
 #include "common/unicode_case.h"
-#include "mb/pg_wchar.h"
 
 /*
  * The maximum number of codepoints that can result from case mapping
 typedef struct
 {
    int16       conditions;
-   pg_wchar    map[NCaseKind][MAX_CASE_EXPANSION];
+   char32_t    map[NCaseKind][MAX_CASE_EXPANSION];
 } pg_special_case;
 
 /*
  * The entry case_map_${kind}[case_index(codepoint)] is the mapping for the
  * given codepoint.
  */
-static const pg_wchar case_map_$kind\[$index\] =
+static const char32_t case_map_$kind\[$index\] =
 {
 EOS
 
  * the offset into the mapping tables.
  */
 static inline uint16
-case_index(pg_wchar cp)
+case_index(char32_t cp)
 {
    /* Fast path for codepoints < $fastpath_limit */
    if (cp < $fastpath_limit)
 
  */
 typedef struct
 {
-   uint32      first;          /* Unicode codepoint */
-   uint32      last;           /* Unicode codepoint */
+   char32_t    first;          /* Unicode codepoint */
+   char32_t    last;           /* Unicode codepoint */
    uint8       category;       /* General Category */
 } pg_category_range;
 
 typedef struct
 {
-   uint32      first;          /* Unicode codepoint */
-   uint32      last;           /* Unicode codepoint */
+   char32_t    first;          /* Unicode codepoint */
+   char32_t    last;           /* Unicode codepoint */
 } pg_unicode_range;
 
 typedef struct
 
 #include "norm_test_table.h"
 
 static char *
-print_wchar_str(const pg_wchar *s)
+print_wchar_str(const char32_t *s)
 {
 #define BUF_DIGITS 50
    static char buf[BUF_DIGITS * 11 + 1];
 }
 
 static int
-pg_wcscmp(const pg_wchar *s1, const pg_wchar *s2)
+pg_wcscmp(const char32_t *s1, const char32_t *s2)
 {
    for (;;)
    {
    {
        for (int form = 0; form < 4; form++)
        {
-           pg_wchar   *result;
+           char32_t   *result;
 
            result = unicode_normalize(form, test->input);
 
 
 /*
  * Map for each case kind.
  */
-static const pg_wchar *const casekind_map[NCaseKind] =
+static const char32_t *const casekind_map[NCaseKind] =
 {
    [CaseLower] = case_map_lower,
    [CaseTitle] = case_map_title,
    [CaseFold] = case_map_fold,
 };
 
-static pg_wchar find_case_map(pg_wchar ucs, const pg_wchar *map);
+static char32_t find_case_map(char32_t ucs, const char32_t *map);
 static size_t convert_case(char *dst, size_t dstsize, const char *src, ssize_t srclen,
                           CaseKind str_casekind, bool full, WordBoundaryNext wbnext,
                           void *wbstate);
-static enum CaseMapResult casemap(pg_wchar u1, CaseKind casekind, bool full,
+static enum CaseMapResult casemap(char32_t u1, CaseKind casekind, bool full,
                                  const char *src, size_t srclen, size_t srcoff,
-                                 pg_wchar *simple, const pg_wchar **special);
+                                 char32_t *simple, const char32_t **special);
 
-pg_wchar
-unicode_lowercase_simple(pg_wchar code)
+char32_t
+unicode_lowercase_simple(char32_t code)
 {
-   pg_wchar    cp = find_case_map(code, case_map_lower);
+   char32_t    cp = find_case_map(code, case_map_lower);
 
    return cp != 0 ? cp : code;
 }
 
-pg_wchar
-unicode_titlecase_simple(pg_wchar code)
+char32_t
+unicode_titlecase_simple(char32_t code)
 {
-   pg_wchar    cp = find_case_map(code, case_map_title);
+   char32_t    cp = find_case_map(code, case_map_title);
 
    return cp != 0 ? cp : code;
 }
 
-pg_wchar
-unicode_uppercase_simple(pg_wchar code)
+char32_t
+unicode_uppercase_simple(char32_t code)
 {
-   pg_wchar    cp = find_case_map(code, case_map_upper);
+   char32_t    cp = find_case_map(code, case_map_upper);
 
    return cp != 0 ? cp : code;
 }
 
-pg_wchar
-unicode_casefold_simple(pg_wchar code)
+char32_t
+unicode_casefold_simple(char32_t code)
 {
-   pg_wchar    cp = find_case_map(code, case_map_fold);
+   char32_t    cp = find_case_map(code, case_map_fold);
 
    return cp != 0 ? cp : code;
 }
 
    while ((srclen < 0 || srcoff < srclen) && src[srcoff] != '\0')
    {
-       pg_wchar    u1 = utf8_to_unicode((unsigned char *) src + srcoff);
+       char32_t    u1 = utf8_to_unicode((unsigned char *) src + srcoff);
        int         u1len = unicode_utf8len(u1);
-       pg_wchar    simple = 0;
-       const pg_wchar *special = NULL;
+       char32_t    simple = 0;
+       const char32_t *special = NULL;
        enum CaseMapResult casemap_result;
 
        if (str_casekind == CaseTitle)
            case CASEMAP_SIMPLE:
                {
                    /* replace with single character */
-                   pg_wchar    u2 = simple;
-                   pg_wchar    u2len = unicode_utf8len(u2);
+                   char32_t    u2 = simple;
+                   char32_t    u2len = unicode_utf8len(u2);
 
                    Assert(special == NULL);
                    if (result_len + u2len <= dstsize)
                Assert(simple == 0);
                for (int i = 0; i < MAX_CASE_EXPANSION && special[i]; i++)
                {
-                   pg_wchar    u2 = special[i];
+                   char32_t    u2 = special[i];
                    size_t      u2len = unicode_utf8len(u2);
 
                    if (result_len + u2len <= dstsize)
    {
        if ((str[i] & 0x80) == 0 || (str[i] & 0xC0) == 0xC0)
        {
-           pg_wchar    curr = utf8_to_unicode(str + i);
+           char32_t    curr = utf8_to_unicode(str + i);
 
            if (pg_u_prop_case_ignorable(curr))
                continue;
    {
        if ((str[i] & 0x80) == 0 || (str[i] & 0xC0) == 0xC0)
        {
-           pg_wchar    curr = utf8_to_unicode(str + i);
+           char32_t    curr = utf8_to_unicode(str + i);
 
            if (pg_u_prop_case_ignorable(curr))
                continue;
  * character without modification.
  */
 static enum CaseMapResult
-casemap(pg_wchar u1, CaseKind casekind, bool full,
+casemap(char32_t u1, CaseKind casekind, bool full,
        const char *src, size_t srclen, size_t srcoff,
-       pg_wchar *simple, const pg_wchar **special)
+       char32_t *simple, const char32_t **special)
 {
    uint16      idx;
 
  * Find entry in simple case map.
  * If the entry does not exist, 0 will be returned.
  */
-static pg_wchar
-find_case_map(pg_wchar ucs, const pg_wchar *map)
+static char32_t
+find_case_map(char32_t ucs, const char32_t *map)
 {
    /* Fast path for codepoints < 0x80 */
    if (ucs < 0x80)
 
 /*-------------------------------------------------------------------------
  * unicode_category.c
  *     Determine general category and character properties of Unicode
- *     characters. Encoding must be UTF8, where we assume that the pg_wchar
+ *     characters. Encoding must be UTF8, where we assume that the char32_t
  *     representation is a code point.
  *
  * Portions Copyright (c) 2017-2025, PostgreSQL Global Development Group
 #define PG_U_CHARACTER_TAB 0x09
 
 static bool range_search(const pg_unicode_range *tbl, size_t size,
-                        pg_wchar code);
+                        char32_t code);
 
 /*
  * Unicode general category for the given codepoint.
  */
 pg_unicode_category
-unicode_category(pg_wchar code)
+unicode_category(char32_t code)
 {
    int         min = 0;
    int         mid;
 }
 
 bool
-pg_u_prop_alphabetic(pg_wchar code)
+pg_u_prop_alphabetic(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_ALPHABETIC;
 }
 
 bool
-pg_u_prop_lowercase(pg_wchar code)
+pg_u_prop_lowercase(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_LOWERCASE;
 }
 
 bool
-pg_u_prop_uppercase(pg_wchar code)
+pg_u_prop_uppercase(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_UPPERCASE;
 }
 
 bool
-pg_u_prop_cased(pg_wchar code)
+pg_u_prop_cased(char32_t code)
 {
    uint32      category_mask;
 
 }
 
 bool
-pg_u_prop_case_ignorable(pg_wchar code)
+pg_u_prop_case_ignorable(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_CASE_IGNORABLE;
 }
 
 bool
-pg_u_prop_white_space(pg_wchar code)
+pg_u_prop_white_space(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_WHITE_SPACE;
 }
 
 bool
-pg_u_prop_hex_digit(pg_wchar code)
+pg_u_prop_hex_digit(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_HEX_DIGIT;
 }
 
 bool
-pg_u_prop_join_control(pg_wchar code)
+pg_u_prop_join_control(char32_t code)
 {
    if (code < 0x80)
        return unicode_opt_ascii[code].properties & PG_U_PROP_JOIN_CONTROL;
  */
 
 bool
-pg_u_isdigit(pg_wchar code, bool posix)
+pg_u_isdigit(char32_t code, bool posix)
 {
    if (posix)
        return ('0' <= code && code <= '9');
 }
 
 bool
-pg_u_isalpha(pg_wchar code)
+pg_u_isalpha(char32_t code)
 {
    return pg_u_prop_alphabetic(code);
 }
 
 bool
-pg_u_isalnum(pg_wchar code, bool posix)
+pg_u_isalnum(char32_t code, bool posix)
 {
    return pg_u_isalpha(code) || pg_u_isdigit(code, posix);
 }
 
 bool
-pg_u_isword(pg_wchar code)
+pg_u_isword(char32_t code)
 {
    uint32      category_mask = PG_U_CATEGORY_MASK(unicode_category(code));
 
 }
 
 bool
-pg_u_isupper(pg_wchar code)
+pg_u_isupper(char32_t code)
 {
    return pg_u_prop_uppercase(code);
 }
 
 bool
-pg_u_islower(pg_wchar code)
+pg_u_islower(char32_t code)
 {
    return pg_u_prop_lowercase(code);
 }
 
 bool
-pg_u_isblank(pg_wchar code)
+pg_u_isblank(char32_t code)
 {
    return code == PG_U_CHARACTER_TAB ||
        unicode_category(code) == PG_U_SPACE_SEPARATOR;
 }
 
 bool
-pg_u_iscntrl(pg_wchar code)
+pg_u_iscntrl(char32_t code)
 {
    return unicode_category(code) == PG_U_CONTROL;
 }
 
 bool
-pg_u_isgraph(pg_wchar code)
+pg_u_isgraph(char32_t code)
 {
    uint32      category_mask = PG_U_CATEGORY_MASK(unicode_category(code));
 
 }
 
 bool
-pg_u_isprint(pg_wchar code)
+pg_u_isprint(char32_t code)
 {
    pg_unicode_category category = unicode_category(code);
 
 }
 
 bool
-pg_u_ispunct(pg_wchar code, bool posix)
+pg_u_ispunct(char32_t code, bool posix)
 {
    uint32      category_mask;
 
 }
 
 bool
-pg_u_isspace(pg_wchar code)
+pg_u_isspace(char32_t code)
 {
    return pg_u_prop_white_space(code);
 }
 
 bool
-pg_u_isxdigit(pg_wchar code, bool posix)
+pg_u_isxdigit(char32_t code, bool posix)
 {
    if (posix)
        return (('0' <= code && code <= '9') ||
  * given table.
  */
 static bool
-range_search(const pg_unicode_range *tbl, size_t size, pg_wchar code)
+range_search(const pg_unicode_range *tbl, size_t size, char32_t code)
 {
    int         min = 0;
    int         mid;
 
  * lookup, while the frontend version uses a binary search.
  */
 static const pg_unicode_decomposition *
-get_code_entry(pg_wchar code)
+get_code_entry(char32_t code)
 {
 #ifndef FRONTEND
    int         h;
  * Get the combining class of the given codepoint.
  */
 static uint8
-get_canonical_class(pg_wchar code)
+get_canonical_class(char32_t code)
 {
    const pg_unicode_decomposition *entry = get_code_entry(code);
 
  * Note: the returned pointer can point to statically allocated buffer, and
  * is only valid until next call to this function!
  */
-static const pg_wchar *
+static const char32_t *
 get_code_decomposition(const pg_unicode_decomposition *entry, int *dec_size)
 {
-   static pg_wchar x;
+   static char32_t x;
 
    if (DECOMPOSITION_IS_INLINE(entry))
    {
        Assert(DECOMPOSITION_SIZE(entry) == 1);
-       x = (pg_wchar) entry->dec_index;
+       x = (char32_t) entry->dec_index;
        *dec_size = 1;
        return &x;
    }
  * are, in turn, decomposable.
  */
 static int
-get_decomposed_size(pg_wchar code, bool compat)
+get_decomposed_size(char32_t code, bool compat)
 {
    const pg_unicode_decomposition *entry;
    int         size = 0;
  * in the array result.
  */
 static void
-decompose_code(pg_wchar code, bool compat, pg_wchar **result, int *current)
+decompose_code(char32_t code, bool compat, char32_t **result, int *current)
 {
    const pg_unicode_decomposition *entry;
    int         i;
                    v,
                    tindex,
                    sindex;
-       pg_wchar   *res = *result;
+       char32_t   *res = *result;
 
        sindex = code - SBASE;
        l = LBASE + sindex / (VCOUNT * TCOUNT);
    if (entry == NULL || DECOMPOSITION_SIZE(entry) == 0 ||
        (!compat && DECOMPOSITION_IS_COMPAT(entry)))
    {
-       pg_wchar   *res = *result;
+       char32_t   *res = *result;
 
        res[*current] = code;
        (*current)++;
    decomp = get_code_decomposition(entry, &dec_size);
    for (i = 0; i < dec_size; i++)
    {
-       pg_wchar    lcode = (pg_wchar) decomp[i];
+       char32_t    lcode = (char32_t) decomp[i];
 
        /* Leave if no more decompositions */
        decompose_code(lcode, compat, result, current);
  * malloc. Or NULL if we run out of memory. In backend, the returned
  * string is palloc'd instead, and OOM is reported with ereport().
  */
-pg_wchar *
-unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input)
+char32_t *
+unicode_normalize(UnicodeNormalizationForm form, const char32_t *input)
 {
    bool        compat = (form == UNICODE_NFKC || form == UNICODE_NFKD);
    bool        recompose = (form == UNICODE_NFC || form == UNICODE_NFKC);
-   pg_wchar   *decomp_chars;
-   pg_wchar   *recomp_chars;
+   char32_t   *decomp_chars;
+   char32_t   *recomp_chars;
    int         decomp_size,
                current_size;
    int         count;
-   const pg_wchar *p;
+   const char32_t *p;
 
    /* variables for recomposition */
    int         last_class;
    for (p = input; *p; p++)
        decomp_size += get_decomposed_size(*p, compat);
 
-   decomp_chars = (pg_wchar *) ALLOC((decomp_size + 1) * sizeof(pg_wchar));
+   decomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
    if (decomp_chars == NULL)
        return NULL;
 
     */
    for (count = 1; count < decomp_size; count++)
    {
-       pg_wchar    prev = decomp_chars[count - 1];
-       pg_wchar    next = decomp_chars[count];
-       pg_wchar    tmp;
+       char32_t    prev = decomp_chars[count - 1];
+       char32_t    next = decomp_chars[count];
+       char32_t    tmp;
        const uint8 prevClass = get_canonical_class(prev);
        const uint8 nextClass = get_canonical_class(next);
 
     * longer than the decomposed one, so make the allocation of the output
     * string based on that assumption.
     */
-   recomp_chars = (pg_wchar *) ALLOC((decomp_size + 1) * sizeof(pg_wchar));
+   recomp_chars = (char32_t *) ALLOC((decomp_size + 1) * sizeof(char32_t));
    if (!recomp_chars)
    {
        FREE(decomp_chars);
 
    for (count = 1; count < decomp_size; count++)
    {
-       pg_wchar    ch = decomp_chars[count];
+       char32_t    ch = decomp_chars[count];
        int         ch_class = get_canonical_class(ch);
-       pg_wchar    composite;
+       char32_t    composite;
 
        if (last_class < ch_class &&
            recompose_code(starter_ch, ch, &composite))
            recomp_chars[target_pos++] = ch;
        }
    }
-   recomp_chars[target_pos] = (pg_wchar) '\0';
+   recomp_chars[target_pos] = (char32_t) '\0';
 
    FREE(decomp_chars);
 
 #ifndef FRONTEND
 
 static const pg_unicode_normprops *
-qc_hash_lookup(pg_wchar ch, const pg_unicode_norminfo *norminfo)
+qc_hash_lookup(char32_t ch, const pg_unicode_norminfo *norminfo)
 {
    int         h;
    uint32      hashkey;
  * Look up the normalization quick check character property
  */
 static UnicodeNormalizationQC
-qc_is_allowed(UnicodeNormalizationForm form, pg_wchar ch)
+qc_is_allowed(UnicodeNormalizationForm form, char32_t ch)
 {
    const pg_unicode_normprops *found = NULL;
 
 }
 
 UnicodeNormalizationQC
-unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const pg_wchar *input)
+unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const char32_t *input)
 {
    uint8       lastCanonicalClass = 0;
    UnicodeNormalizationQC result = UNICODE_NORM_QC_YES;
    if (form == UNICODE_NFD || form == UNICODE_NFKD)
        return UNICODE_NORM_QC_MAYBE;
 
-   for (const pg_wchar *p = input; *p; p++)
+   for (const char32_t *p = input; *p; p++)
    {
-       pg_wchar    ch = *p;
+       char32_t    ch = *p;
        uint8       canonicalClass;
        UnicodeNormalizationQC check;
 
 
  *
  * No error checks here, c must point to a long-enough string.
  */
-static pg_wchar
+static char32_t
 utf8_to_unicode(const unsigned char *c)
 {
    if ((*c & 0x80) == 0)
-       return (pg_wchar) c[0];
+       return (char32_t) c[0];
    else if ((*c & 0xe0) == 0xc0)
-       return (pg_wchar) (((c[0] & 0x1f) << 6) |
+       return (char32_t) (((c[0] & 0x1f) << 6) |
                           (c[1] & 0x3f));
    else if ((*c & 0xf0) == 0xe0)
-       return (pg_wchar) (((c[0] & 0x0f) << 12) |
+       return (char32_t) (((c[0] & 0x0f) << 12) |
                           ((c[1] & 0x3f) << 6) |
                           (c[2] & 0x3f));
    else if ((*c & 0xf8) == 0xf0)
-       return (pg_wchar) (((c[0] & 0x07) << 18) |
+       return (char32_t) (((c[0] & 0x07) << 18) |
                           ((c[1] & 0x3f) << 12) |
                           ((c[2] & 0x3f) << 6) |
                           (c[3] & 0x3f));
 
 /* /port compatibility functions */
 #include "port.h"
 
+/*
+ * char16_t and char32_t
+ *      Unicode code points.
+ *
+ * uchar.h should always be available in C11, but it's not available on
+ * Mac. However, these types are keywords in C++11, so when using C++, we
+ * can't redefine the types.
+ *
+ * XXX: when uchar.h is available everywhere, we can remove this check and
+ * just include uchar.h unconditionally.
+ *
+ * XXX: this section is out of place because uchar.h needs to be included
+ * after port.h, due to an interaction with win32_port.h in some cases.
+ */
+#ifdef HAVE_UCHAR_H
+#include <uchar.h>
+#else
+#ifndef __cplusplus
+typedef uint16_t char16_t;
+typedef uint32_t char32_t;
+#endif
+#endif
+
 /* IWYU pragma: end_exports */
 
 #endif                         /* C_H */
 
 #ifndef UNICODE_CASE_H
 #define UNICODE_CASE_H
 
-#include "mb/pg_wchar.h"
-
 typedef size_t (*WordBoundaryNext) (void *wbstate);
 
-pg_wchar   unicode_lowercase_simple(pg_wchar code);
-pg_wchar   unicode_titlecase_simple(pg_wchar code);
-pg_wchar   unicode_uppercase_simple(pg_wchar code);
-pg_wchar   unicode_casefold_simple(pg_wchar code);
+char32_t   unicode_lowercase_simple(char32_t code);
+char32_t   unicode_titlecase_simple(char32_t code);
+char32_t   unicode_uppercase_simple(char32_t code);
+char32_t   unicode_casefold_simple(char32_t code);
 size_t     unicode_strlower(char *dst, size_t dstsize, const char *src,
                             ssize_t srclen, bool full);
 size_t     unicode_strtitle(char *dst, size_t dstsize, const char *src,
 
  */
 
 #include "common/unicode_case.h"
-#include "mb/pg_wchar.h"
 
 /*
  * The maximum number of codepoints that can result from case mapping
 typedef struct
 {
    int16       conditions;
-   pg_wchar    map[NCaseKind][MAX_CASE_EXPANSION];
+   char32_t    map[NCaseKind][MAX_CASE_EXPANSION];
 } pg_special_case;
 
 /*
  * The entry case_map_lower[case_index(codepoint)] is the mapping for the
  * given codepoint.
  */
-static const pg_wchar case_map_lower[1704] =
+static const char32_t case_map_lower[1704] =
 {
    0x000000,                   /* reserved */
    0x000000,                   /* U+000000 */
  * The entry case_map_title[case_index(codepoint)] is the mapping for the
  * given codepoint.
  */
-static const pg_wchar case_map_title[1704] =
+static const char32_t case_map_title[1704] =
 {
    0x000000,                   /* reserved */
    0x000000,                   /* U+000000 */
  * The entry case_map_upper[case_index(codepoint)] is the mapping for the
  * given codepoint.
  */
-static const pg_wchar case_map_upper[1704] =
+static const char32_t case_map_upper[1704] =
 {
    0x000000,                   /* reserved */
    0x000000,                   /* U+000000 */
  * The entry case_map_fold[case_index(codepoint)] is the mapping for the
  * given codepoint.
  */
-static const pg_wchar case_map_fold[1704] =
+static const char32_t case_map_fold[1704] =
 {
    0x000000,                   /* reserved */
    0x000000,                   /* U+000000 */
  * the offset into the mapping tables.
  */
 static inline uint16
-case_index(pg_wchar cp)
+case_index(char32_t cp)
 {
    /* Fast path for codepoints < 0x0588 */
    if (cp < 0x0588)
 
 #ifndef UNICODE_CATEGORY_H
 #define UNICODE_CATEGORY_H
 
-#include "mb/pg_wchar.h"
-
 /*
  * Unicode General Category Values
  *
    PG_U_FINAL_PUNCTUATION = 29 /* Pf */
 } pg_unicode_category;
 
-extern pg_unicode_category unicode_category(pg_wchar code);
+extern pg_unicode_category unicode_category(char32_t code);
 extern const char *unicode_category_string(pg_unicode_category category);
 extern const char *unicode_category_abbrev(pg_unicode_category category);
 
-extern bool pg_u_prop_alphabetic(pg_wchar code);
-extern bool pg_u_prop_lowercase(pg_wchar code);
-extern bool pg_u_prop_uppercase(pg_wchar code);
-extern bool pg_u_prop_cased(pg_wchar code);
-extern bool pg_u_prop_case_ignorable(pg_wchar code);
-extern bool pg_u_prop_white_space(pg_wchar code);
-extern bool pg_u_prop_hex_digit(pg_wchar code);
-extern bool pg_u_prop_join_control(pg_wchar code);
+extern bool pg_u_prop_alphabetic(char32_t code);
+extern bool pg_u_prop_lowercase(char32_t code);
+extern bool pg_u_prop_uppercase(char32_t code);
+extern bool pg_u_prop_cased(char32_t code);
+extern bool pg_u_prop_case_ignorable(char32_t code);
+extern bool pg_u_prop_white_space(char32_t code);
+extern bool pg_u_prop_hex_digit(char32_t code);
+extern bool pg_u_prop_join_control(char32_t code);
 
-extern bool pg_u_isdigit(pg_wchar code, bool posix);
-extern bool pg_u_isalpha(pg_wchar code);
-extern bool pg_u_isalnum(pg_wchar code, bool posix);
-extern bool pg_u_isword(pg_wchar code);
-extern bool pg_u_isupper(pg_wchar code);
-extern bool pg_u_islower(pg_wchar code);
-extern bool pg_u_isblank(pg_wchar code);
-extern bool pg_u_iscntrl(pg_wchar code);
-extern bool pg_u_isgraph(pg_wchar code);
-extern bool pg_u_isprint(pg_wchar code);
-extern bool pg_u_ispunct(pg_wchar code, bool posix);
-extern bool pg_u_isspace(pg_wchar code);
-extern bool pg_u_isxdigit(pg_wchar code, bool posix);
+extern bool pg_u_isdigit(char32_t code, bool posix);
+extern bool pg_u_isalpha(char32_t code);
+extern bool pg_u_isalnum(char32_t code, bool posix);
+extern bool pg_u_isword(char32_t code);
+extern bool pg_u_isupper(char32_t code);
+extern bool pg_u_islower(char32_t code);
+extern bool pg_u_isblank(char32_t code);
+extern bool pg_u_iscntrl(char32_t code);
+extern bool pg_u_isgraph(char32_t code);
+extern bool pg_u_isprint(char32_t code);
+extern bool pg_u_ispunct(char32_t code, bool posix);
+extern bool pg_u_isspace(char32_t code);
+extern bool pg_u_isxdigit(char32_t code, bool posix);
 
 #endif                         /* UNICODE_CATEGORY_H */
 
  */
 typedef struct
 {
-   uint32      first;          /* Unicode codepoint */
-   uint32      last;           /* Unicode codepoint */
+   char32_t    first;          /* Unicode codepoint */
+   char32_t    last;           /* Unicode codepoint */
    uint8       category;       /* General Category */
 } pg_category_range;
 
 typedef struct
 {
-   uint32      first;          /* Unicode codepoint */
-   uint32      last;           /* Unicode codepoint */
+   char32_t    first;          /* Unicode codepoint */
+   char32_t    last;           /* Unicode codepoint */
 } pg_unicode_range;
 
 typedef struct
 
 #ifndef UNICODE_NORM_H
 #define UNICODE_NORM_H
 
-#include "mb/pg_wchar.h"
-
 typedef enum
 {
    UNICODE_NFC = 0,
    UNICODE_NORM_QC_MAYBE = -1,
 } UnicodeNormalizationQC;
 
-extern pg_wchar *unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input);
+extern char32_t *unicode_normalize(UnicodeNormalizationForm form, const char32_t *input);
 
-extern UnicodeNormalizationQC unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const pg_wchar *input);
+extern UnicodeNormalizationQC unicode_is_normalized_quickcheck(UnicodeNormalizationForm form, const char32_t *input);
 
 #endif                         /* UNICODE_NORM_H */
 
  * Some handy functions for Unicode-specific tests.
  */
 static inline bool
-is_valid_unicode_codepoint(pg_wchar c)
+is_valid_unicode_codepoint(char32_t c)
 {
    return (c > 0 && c <= 0x10FFFF);
 }
 
 static inline bool
-is_utf16_surrogate_first(pg_wchar c)
+is_utf16_surrogate_first(char32_t c)
 {
    return (c >= 0xD800 && c <= 0xDBFF);
 }
 
 static inline bool
-is_utf16_surrogate_second(pg_wchar c)
+is_utf16_surrogate_second(char32_t c)
 {
    return (c >= 0xDC00 && c <= 0xDFFF);
 }
 
-static inline pg_wchar
-surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second)
+static inline char32_t
+surrogate_pair_to_codepoint(char16_t first, char16_t second)
 {
    return ((first & 0x3FF) << 10) + 0x10000 + (second & 0x3FF);
 }
  *
  * No error checks here, c must point to a long-enough string.
  */
-static inline pg_wchar
+static inline char32_t
 utf8_to_unicode(const unsigned char *c)
 {
    if ((*c & 0x80) == 0)
-       return (pg_wchar) c[0];
+       return (char32_t) c[0];
    else if ((*c & 0xe0) == 0xc0)
-       return (pg_wchar) (((c[0] & 0x1f) << 6) |
+       return (char32_t) (((c[0] & 0x1f) << 6) |
                           (c[1] & 0x3f));
    else if ((*c & 0xf0) == 0xe0)
-       return (pg_wchar) (((c[0] & 0x0f) << 12) |
+       return (char32_t) (((c[0] & 0x0f) << 12) |
                           ((c[1] & 0x3f) << 6) |
                           (c[2] & 0x3f));
    else if ((*c & 0xf8) == 0xf0)
-       return (pg_wchar) (((c[0] & 0x07) << 18) |
+       return (char32_t) (((c[0] & 0x07) << 18) |
                           ((c[1] & 0x3f) << 12) |
                           ((c[2] & 0x3f) << 6) |
                           (c[3] & 0x3f));
  * unicode_utf8len(c) bytes available.
  */
 static inline unsigned char *
-unicode_to_utf8(pg_wchar c, unsigned char *utf8string)
+unicode_to_utf8(char32_t c, unsigned char *utf8string)
 {
    if (c <= 0x7F)
    {
  * Number of bytes needed to represent the given char in UTF8.
  */
 static inline int
-unicode_utf8len(pg_wchar c)
+unicode_utf8len(char32_t c)
 {
    if (c <= 0x7F)
        return 1;
 extern bool is_encoding_supported_by_icu(int encoding);
 extern const char *get_encoding_name_for_icu(int encoding);
 
-extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string);
-extern pg_wchar utf8_to_unicode(const unsigned char *c);
+extern unsigned char *unicode_to_utf8(char32_t c, unsigned char *utf8string);
+extern char32_t utf8_to_unicode(const unsigned char *c);
 extern bool pg_utf8_islegal(const unsigned char *source, int length);
 extern int pg_utf_mblen(const unsigned char *s);
 extern int pg_mule_mblen(const unsigned char *s);
 extern char *pg_any_to_server(const char *s, int len, int encoding);
 extern char *pg_server_to_any(const char *s, int len, int encoding);
 
-extern void pg_unicode_to_server(pg_wchar c, unsigned char *s);
-extern bool pg_unicode_to_server_noerror(pg_wchar c, unsigned char *s);
+extern void pg_unicode_to_server(char32_t c, unsigned char *s);
+extern bool pg_unicode_to_server_noerror(char32_t c, unsigned char *s);
 
 extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc);
 extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc);
 
 /* Define to 1 if you have the <termios.h> header file. */
 #undef HAVE_TERMIOS_H
 
+/* Define to 1 if you have the <uchar.h> header file. */
+#undef HAVE_UCHAR_H
+
 /* Define to 1 if curl_global_init() is guaranteed to be thread-safe. */
 #undef HAVE_THREADSAFE_CURL_GLOBAL_INIT
 
 
 cb_options
 cb_tablespace
 cb_tablespace_mapping
+char16_t
+char32_t
 check_agg_arguments_context
 check_function_callback
 check_network_data