v5-0007-Add-test-module-for-libc-collation-provider-hook.patch
text/x-patch
Filename: v5-0007-Add-test-module-for-libc-collation-provider-hook.patch
Type: text/x-patch
Part: 0
Patch
Format: format-patch
Series: patch v5-0007
Subject: Add test module for libc collation provider hook.
| File | + | − |
|---|---|---|
| src/test/modules/test_collation_lib_hooks/Makefile | 9 | 1 |
| src/test/modules/test_collation_lib_hooks/meson.build | 10 | 0 |
| src/test/modules/test_collation_lib_hooks/t/002_libc.pl | 157 | 0 |
| src/test/modules/test_collation_lib_hooks/t/003_libc_mixed_collation.pl | 66 | 0 |
| src/test/modules/test_collation_lib_hooks/t/004_libc_version.pl | 66 | 0 |
| src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c | 7 | 1 |
| src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.h | 4 | 0 |
| src/test/modules/test_collation_lib_hooks/test_libc_hook.c | 555 | 0 |
From 9cc37a73f7094a871662835074a774acc1205d1e Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 21 Dec 2022 19:52:23 -0800
Subject: [PATCH v5 7/7] Add test module for libc collation provider hook.
---
.../modules/test_collation_lib_hooks/Makefile | 10 +-
.../test_collation_lib_hooks/meson.build | 10 +
.../test_collation_lib_hooks/t/002_libc.pl | 157 +++++
.../t/003_libc_mixed_collation.pl | 66 +++
.../t/004_libc_version.pl | 66 +++
.../test_collation_lib_hooks.c | 8 +-
.../test_collation_lib_hooks.h | 4 +
.../test_collation_lib_hooks/test_libc_hook.c | 555 ++++++++++++++++++
8 files changed, 874 insertions(+), 2 deletions(-)
create mode 100644 src/test/modules/test_collation_lib_hooks/t/002_libc.pl
create mode 100644 src/test/modules/test_collation_lib_hooks/t/003_libc_mixed_collation.pl
create mode 100644 src/test/modules/test_collation_lib_hooks/t/004_libc_version.pl
create mode 100644 src/test/modules/test_collation_lib_hooks/test_libc_hook.c
diff --git a/src/test/modules/test_collation_lib_hooks/Makefile b/src/test/modules/test_collation_lib_hooks/Makefile
index 05948e555a..a800c9ea74 100644
--- a/src/test/modules/test_collation_lib_hooks/Makefile
+++ b/src/test/modules/test_collation_lib_hooks/Makefile
@@ -3,7 +3,7 @@
MODULE_big = test_collation_lib_hooks
OBJS = \
$(WIN32RES) \
- test_collation_lib_hooks.o test_icu_hook.o
+ test_collation_lib_hooks.o test_icu_hook.o test_libc_hook.o
PGFILEDESC = "test_collation_lib_hooks - test collation provider library hooks"
EXTENSION = test_collation_lib_hooks
@@ -22,3 +22,11 @@ include $(top_srcdir)/contrib/contrib-global.mk
endif
export with_icu
+
+# TODO set environment variables:
+# determine if built with glibc
+# determine if LC_VERSION_MASK is defined
+
+ifeq ($(PORTNAME), win32)
+ export win32 = yes
+endif
diff --git a/src/test/modules/test_collation_lib_hooks/meson.build b/src/test/modules/test_collation_lib_hooks/meson.build
index 56b32b6cd1..a588751cc1 100644
--- a/src/test/modules/test_collation_lib_hooks/meson.build
+++ b/src/test/modules/test_collation_lib_hooks/meson.build
@@ -2,6 +2,7 @@
test_collation_lib_hooks_sources = files(
'test_collation_lib_hooks.c',
+ 'test_libc_hook.c',
'test_icu_hook.c',
)
@@ -22,6 +23,9 @@ install_data(
kwargs: contrib_data_args,
)
+have_lc_version_mask = cc.has_header_symbol('locale.h', 'LC_VERSION_MASK')
+glibc = cc.has_header_symbol('features.h', '__GLIBC__')
+
tests += {
'name': 'test_collation_lib_hooks',
'sd': meson.current_source_dir(),
@@ -29,9 +33,15 @@ tests += {
'tap': {
'tests': [
't/001_icu.pl',
+ 't/002_libc.pl',
+ 't/003_libc_mixed_collation.pl',
+ 't/004_libc_version.pl',
],
'env': {
'with_icu': icu.found() ? 'yes' : 'no',
+ 'win32': (host_system == 'windows') ? 'yes' : 'no',
+ 'glibc': glibc ? 'yes' : 'no',
+ 'have_lc_version_mask': have_lc_version_mask ? 'yes' : 'no',
},
},
}
diff --git a/src/test/modules/test_collation_lib_hooks/t/002_libc.pl b/src/test/modules/test_collation_lib_hooks/t/002_libc.pl
new file mode 100644
index 0000000000..e43a0916d3
--- /dev/null
+++ b/src/test/modules/test_collation_lib_hooks/t/002_libc.pl
@@ -0,0 +1,157 @@
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+
+$node->init;
+$node->append_conf(
+ 'postgresql.conf', q{
+shared_preload_libraries = 'test_collation_lib_hooks'
+});
+$node->start;
+
+# setup
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_asc (PROVIDER=libc, LOCALE='ASC')]);
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_desc (PROVIDER=libc, LOCALE='DESC')]);
+
+$node->safe_psql('postgres', qq[CREATE TABLE strings(t text)]);
+$node->safe_psql('postgres',
+ qq[INSERT INTO strings VALUES ('aBcD'), ('fGhI'), ('wXyZ')]);
+
+my $sort_asc_expected = "aBcD
+fGhI
+wXyZ";
+
+my $sort_desc_expected = "wXyZ
+fGhI
+aBcD";
+
+# test comparison
+
+my $comparison_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_asc < 'wXyZ' COLLATE test_asc]);
+is($comparison_asc, 't',
+ 'correct comparison'
+);
+
+# test desc comparison
+
+my $comparison_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_desc < 'wXyZ' COLLATE test_desc]);
+is($comparison_desc, 'f',
+ 'correct desc comparison'
+);
+
+# test asc sort with trust_strxfrm = false
+
+my $sort_asc =
+ $node->safe_psql('postgres',
+ qq[SET trust_strxfrm = false;
+ SELECT t FROM strings ORDER BY t COLLATE test_asc]);
+is($sort_asc, $sort_asc_expected,
+ 'correct ascending sort (trust_strxfrm = false)'
+);
+
+# test desc sort with trust_strxfrm = false
+
+my $sort_desc =
+ $node->safe_psql('postgres',
+ qq[SET trust_strxfrm = false;
+ SELECT t FROM strings ORDER BY t COLLATE test_desc]);
+is($sort_desc, $sort_desc_expected,
+ 'correct descending sort (trust_strxfrm = false)'
+);
+
+# test asc sort with trust_strxfrm = true
+
+my $strxfrm_asc =
+ $node->safe_psql('postgres',
+ qq[SET trust_strxfrm = true;
+ SELECT t FROM strings ORDER BY t COLLATE test_asc]);
+is($strxfrm_asc, $sort_asc_expected,
+ 'correct ascending sort (trust_strxfrm = true)'
+);
+
+# test desc sort with trust_strxfrm = true
+
+my $strxfrm_desc =
+ $node->safe_psql('postgres',
+ qq[SET trust_strxfrm = true;
+ SELECT t FROM strings ORDER BY t COLLATE test_desc]);
+is($strxfrm_desc, $sort_desc_expected,
+ 'correct descending sort (trust_strxfrm = true)'
+);
+
+# test lower/upper
+
+my $tcase =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_asc),
+ upper('aBcDfgHiwXyZ' collate test_asc)]);
+is($tcase, 'abcdfghiwxyz|ABCDFGHIWXYZ',
+ 'correct lowercase and uppercase'
+);
+
+# test desc lower/upper
+
+my $tcase_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_desc),
+ upper('aBcDfgHiwXyZ' collate test_desc)]);
+is($tcase_desc, 'ABCDFGHIWXYZ|abcdfghiwxyz',
+ 'correct desc lowercase and uppercase'
+);
+
+if ($ENV{win32} ne 'yes') {
+ $node->safe_psql('postgres',
+ qq[CREATE COLLATION test_mixed_asc_desc
+ (PROVIDER=libc, LC_COLLATE='ASC', LC_CTYPE='DESC')]);
+ $node->safe_psql('postgres',
+ qq[CREATE COLLATION test_mixed_desc_asc
+ (PROVIDER=libc, LC_COLLATE='DESC', LC_CTYPE='ASC')]);
+
+ my $mcomparison_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_mixed_asc_desc <
+ 'wXyZ' COLLATE test_mixed_asc_desc]);
+ is($mcomparison_asc, 't',
+ 'correct mixed asc/desc comparison'
+ );
+
+ my $mcomparison_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_mixed_desc_asc <
+ 'wXyZ' COLLATE test_mixed_desc_asc]);
+ is($mcomparison_desc, 'f',
+ 'correct mixed desc/asc comparison'
+ );
+
+ my $mcase_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_mixed_asc_desc),
+ upper('aBcDfgHiwXyZ' collate test_mixed_asc_desc)]);
+ is($mcase_asc, 'ABCDFGHIWXYZ|abcdfghiwxyz',
+ 'correct case mixed asc/desc'
+ );
+
+ my $mcase_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_mixed_desc_asc),
+ upper('aBcDfgHiwXyZ' collate test_mixed_desc_asc)]);
+ is($mcase_desc, 'abcdfghiwxyz|ABCDFGHIWXYZ',
+ 'correct case mixed desc/asc'
+ );
+}
+
+$node->stop;
+done_testing();
diff --git a/src/test/modules/test_collation_lib_hooks/t/003_libc_mixed_collation.pl b/src/test/modules/test_collation_lib_hooks/t/003_libc_mixed_collation.pl
new file mode 100644
index 0000000000..2ef18f797c
--- /dev/null
+++ b/src/test/modules/test_collation_lib_hooks/t/003_libc_mixed_collation.pl
@@ -0,0 +1,66 @@
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# Test mixed collations with differing lc_collate/lc_ctype
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+if ($ENV{win32} eq 'yes')
+{
+ plan skip_all => 'windows does not support mixed libc collations';
+}
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+
+$node->init;
+$node->append_conf(
+ 'postgresql.conf', q{
+shared_preload_libraries = 'test_collation_lib_hooks'
+});
+$node->start;
+
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_mixed_asc_desc
+ (PROVIDER=libc, LC_COLLATE='ASC', LC_CTYPE='DESC')]);
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_mixed_desc_asc
+ (PROVIDER=libc, LC_COLLATE='DESC', LC_CTYPE='ASC')]);
+
+my $mcomparison_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_mixed_asc_desc <
+ 'wXyZ' COLLATE test_mixed_asc_desc]);
+is($mcomparison_asc, 't',
+ 'correct mixed asc/desc comparison'
+);
+
+my $mcomparison_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT 'aBcD' COLLATE test_mixed_desc_asc <
+ 'wXyZ' COLLATE test_mixed_desc_asc]);
+is($mcomparison_desc, 'f',
+'correct mixed desc/asc comparison'
+);
+
+my $mcase_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_mixed_asc_desc),
+ upper('aBcDfgHiwXyZ' collate test_mixed_asc_desc)]);
+is($mcase_asc, 'ABCDFGHIWXYZ|abcdfghiwxyz',
+ 'correct case mixed asc/desc'
+);
+
+my $mcase_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT lower('aBcDfgHiwXyZ' collate test_mixed_desc_asc),
+ upper('aBcDfgHiwXyZ' collate test_mixed_desc_asc)]);
+is($mcase_desc, 'abcdfghiwxyz|ABCDFGHIWXYZ',
+ 'correct case mixed desc/asc'
+);
+
+$node->stop;
+done_testing();
diff --git a/src/test/modules/test_collation_lib_hooks/t/004_libc_version.pl b/src/test/modules/test_collation_lib_hooks/t/004_libc_version.pl
new file mode 100644
index 0000000000..201fa98e6b
--- /dev/null
+++ b/src/test/modules/test_collation_lib_hooks/t/004_libc_version.pl
@@ -0,0 +1,66 @@
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# Test collation versions (platform-specific)
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $version_asc_expected;
+my $version_desc_expected;
+
+if ($ENV{glibc} eq 'yes') {
+ $version_asc_expected = '3.14159';
+ $version_desc_expected = '3.14159';
+} elsif ($ENV{have_lc_version_mask} eq 'yes') {
+ $version_asc_expected = '3.14';
+ $version_desc_expected = '6.28';
+} elsif ($ENV{win32} eq 'yes') {
+ $version_asc_expected = '3.14,3.14';
+ $version_desc_expected = '6.28,6.28';
+} else {
+ plan skip_all => 'platform does not support libc collation versions';
+}
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+
+$node->init;
+$node->append_conf(
+ 'postgresql.conf', q{
+shared_preload_libraries = 'test_collation_lib_hooks'
+});
+$node->start;
+
+# setup
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_asc (PROVIDER=libc, LOCALE='ASC')]);
+$node->safe_psql('postgres',
+ qq[CREATE COLLATION test_desc (PROVIDER=libc, LOCALE='DESC')]);
+
+$node->safe_psql('postgres', qq[CREATE TABLE strings(t text)]);
+$node->safe_psql('postgres',
+ qq[INSERT INTO strings VALUES ('aBcD'), ('fGhI'), ('wXyZ')]);
+
+# check versions
+
+my $pg_version = $node->safe_psql('postgres', qq[SELECT version()]);
+
+my $version_asc =
+ $node->safe_psql('postgres',
+ qq[SELECT collversion FROM pg_collation WHERE collname='test_asc']);
+is($version_asc, $version_asc_expected,
+ "collation test_asc has correct version $version_asc_expected"
+);
+
+my $version_desc =
+ $node->safe_psql('postgres',
+ qq[SELECT collversion FROM pg_collation WHERE collname='test_desc']);
+is($version_desc, $version_desc_expected,
+ "collation test_desc has correct version $version_desc_expected"
+);
+
+$node->stop;
+done_testing();
diff --git a/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c b/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c
index 599ec61239..5650840b4d 100644
--- a/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c
+++ b/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c
@@ -9,7 +9,7 @@
* IDENTIFICATION
* src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.c
*
- * Test implementation of icu-like collation provider.
+ * Test implementations of libc-like and icu-like collation providers.
*
* -------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "test_collation_lib_hooks.h"
+static get_libc_library_hook_type prev_get_libc_library_hook = NULL;
#ifdef USE_ICU
static get_icu_library_hook_type prev_get_icu_library_hook = NULL;
#endif
@@ -36,8 +37,13 @@ _PG_init(void)
if (!process_shared_preload_libraries_in_progress)
ereport(ERROR, (errmsg("test_collation_lib_hooks must be loaded via shared_preload_libraries")));
+ prev_get_libc_library_hook = get_libc_library_hook;
+ get_libc_library_hook = test_get_libc_library;
+
#ifdef USE_ICU
prev_get_icu_library_hook = get_icu_library_hook;
get_icu_library_hook = test_get_icu_library;
#endif
+
+ init_libc_hook();
}
diff --git a/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.h b/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.h
index e6ee457ab3..94ea943b97 100644
--- a/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.h
+++ b/src/test/modules/test_collation_lib_hooks/test_collation_lib_hooks.h
@@ -20,6 +20,10 @@
#include "utils/pg_locale.h"
#include "utils/pg_locale_internal.h"
+extern void init_libc_hook(void);
+extern pg_libc_library *test_get_libc_library(const char *collate,
+ const char *ctype,
+ const char *version);
#ifdef USE_ICU
extern pg_icu_library *test_get_icu_library(const char *locale,
const char *version);
diff --git a/src/test/modules/test_collation_lib_hooks/test_libc_hook.c b/src/test/modules/test_collation_lib_hooks/test_libc_hook.c
new file mode 100644
index 0000000000..a8eb5d8dd2
--- /dev/null
+++ b/src/test/modules/test_collation_lib_hooks/test_libc_hook.c
@@ -0,0 +1,555 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_libc_hook.c
+ * Code for testing collation provider libc hook.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_collation_lib_hooks/test_libc_hook.c
+ *
+ * Implements a custom libc-like collation provider library for testing the
+ * hooks. It accepts any collation name requested. All behave exactly like the
+ * "C" locale, except for the locale named "DESC", which reverses the sort
+ * order and reverses uppercase/lowercase behavior.
+ *
+ * The version is always reported as 3.14159, so loading it will cause a
+ * version mismatch warning.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "test_collation_lib_hooks.h"
+
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
+#ifdef WIN32
+#include <shlwapi.h>
+#endif
+
+#define LOCALE_NAME_LEN 64
+
+typedef struct test_locale_t
+{
+ bool reverse_sort;
+ bool reverse_case;
+ char lc_collate[LOCALE_NAME_LEN];
+ char lc_ctype[LOCALE_NAME_LEN];
+} test_locale_t;
+
+static pg_libc_library *test_libc_library = NULL;
+static test_locale_t current_setlocale = {
+ .lc_collate = "C",
+ .lc_ctype = "C",
+ .reverse_sort = false,
+ .reverse_case = false
+};
+
+#ifdef HAVE_LOCALE_T
+#ifndef WIN32
+static test_locale_t *current_uselocale = ¤t_setlocale;
+#endif
+#endif
+
+#ifdef HAVE_LOCALE_T
+static locale_t c_locale_t = NULL;
+#endif
+
+void
+init_libc_hook()
+{
+#ifdef HAVE_LOCALE_T
+#ifndef WIN32
+ c_locale_t = newlocale(LC_ALL_MASK, "C", NULL);
+#else
+ c_locale_t = _create_locale(LC_ALL, "C");
+#endif
+#endif
+}
+
+#ifdef HAVE_LOCALE_T
+static test_locale_t *
+current_locale(void)
+{
+#ifndef WIN32
+ return current_uselocale;
+#else
+ return ¤t_setlocale;
+#endif
+}
+#endif
+
+static bool
+locale_is_reverse(const char *locale)
+{
+ if (strcmp(locale, "DESC") == 0)
+ return true;
+ else
+ return false;
+}
+
+static const char *
+test_libc_version(void)
+{
+ return "3.14159";
+}
+
+#ifdef WIN32
+bool
+test_GetNLSVersionEx(NLS_FUNCTION function, LPCWSTR lpLocaleName,
+ LPNLSVERSIONINFOEX lpVersionInformation)
+{
+ Assert(function == COMPARE_STRING);
+ if (wcscmp(lpLocaleName, L"DESC") == 0)
+ {
+ lpVersionInformation->dwNLSVersion = (6 << 8) | 28;
+ lpVersionInformation->dwDefinedVersion = (6 << 8) | 28;
+ }
+ else
+ {
+ lpVersionInformation->dwNLSVersion = (3 << 8) | 14;
+ lpVersionInformation->dwDefinedVersion = (3 << 8) | 14;
+ }
+
+ return true;
+}
+#endif
+
+static char *
+test_setlocale(int category, const char *locale)
+{
+ Assert (category == LC_COLLATE || category == LC_CTYPE ||
+ category == LC_ALL);
+
+ if (category == LC_ALL)
+ {
+ if (locale)
+ {
+ current_setlocale.reverse_sort = locale_is_reverse(locale);
+ current_setlocale.reverse_case = locale_is_reverse(locale);
+ strncpy(current_setlocale.lc_collate, locale, LOCALE_NAME_LEN);
+ strncpy(current_setlocale.lc_ctype, locale, LOCALE_NAME_LEN);
+ }
+ return current_setlocale.lc_collate;
+ }
+ else if (category == LC_COLLATE)
+ {
+ if (locale)
+ {
+ current_setlocale.reverse_sort = locale_is_reverse(locale);
+ strncpy(current_setlocale.lc_collate, locale, LOCALE_NAME_LEN);
+ }
+
+ return current_setlocale.lc_collate;
+ }
+ else if (category == LC_CTYPE)
+ {
+ if (locale)
+ {
+ current_setlocale.reverse_case = locale_is_reverse(locale);
+ strncpy(current_setlocale.lc_ctype, locale, LOCALE_NAME_LEN);
+ }
+
+ return current_setlocale.lc_ctype;
+ }
+
+ return NULL;
+}
+
+#ifdef HAVE_LOCALE_T
+
+#ifndef WIN32
+
+static locale_t
+test_newlocale(int category, const char *locale, locale_t baselocale_t)
+{
+ test_locale_t *newloc;
+
+ Assert(baselocale_t != LC_GLOBAL_LOCALE);
+ Assert((test_locale_t *) baselocale_t != ¤t_setlocale);
+
+ if (baselocale_t == NULL)
+ {
+ newloc = MemoryContextAlloc(TopMemoryContext,
+ sizeof(test_locale_t));
+ strncpy(newloc->lc_collate, "C", LOCALE_NAME_LEN);
+ strncpy(newloc->lc_ctype, "C", LOCALE_NAME_LEN);
+ newloc->reverse_sort = false;
+ newloc->reverse_case = false;
+ }
+ else
+ newloc = (test_locale_t *) baselocale_t;
+
+ if ((category & LC_COLLATE_MASK) != 0)
+ {
+ newloc->reverse_sort = locale_is_reverse(locale);
+ strncpy(newloc->lc_collate, locale, LOCALE_NAME_LEN);
+ }
+ if ((category & LC_CTYPE_MASK) != 0)
+ {
+ newloc->reverse_case = locale_is_reverse(locale);
+ strncpy(newloc->lc_ctype, locale, LOCALE_NAME_LEN);
+ }
+
+ return (locale_t) newloc;
+}
+
+static void
+test_freelocale(locale_t loc)
+{
+ Assert(loc != LC_GLOBAL_LOCALE);
+ Assert((test_locale_t *)loc != ¤t_setlocale);
+ pfree(loc);
+}
+
+static locale_t
+test_uselocale(locale_t loc)
+{
+ test_locale_t *result = current_uselocale;
+
+ if (loc != NULL)
+ {
+ if (loc == LC_GLOBAL_LOCALE)
+ current_uselocale = ¤t_setlocale;
+ else
+ current_uselocale = (test_locale_t *) loc;
+ }
+
+ if (result == ¤t_setlocale)
+ return LC_GLOBAL_LOCALE;
+ else
+ return (locale_t) result;
+}
+
+#ifdef LC_VERSION_MASK
+static const char *
+test_querylocale(int mask, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *)locale;
+ Assert((mask & LC_VERSION_MASK) != 0);
+ if (testlocale->reverse_sort)
+ return "6.28";
+ else
+ return "3.14";
+}
+#endif /* LC_VERSION_MASK */
+
+#else /* WIN32 */
+static locale_t
+_test_create_locale(int category, const char *locale)
+{
+ test_locale_t *newloc;
+
+ newloc = MemoryContextAlloc(TopMemoryContext,
+ sizeof(test_locale_t));
+ strncpy(newloc->lc_collate, "C", LOCALE_NAME_LEN);
+ strncpy(newloc->lc_ctype, "C", LOCALE_NAME_LEN);
+ newloc->reverse_sort = false;
+ newloc->reverse_case = false;
+
+ if (category == LC_ALL || category == LC_COLLATE)
+ {
+ if (locale_is_reverse(locale))
+ newloc->reverse_sort = true;
+ else
+ newloc->reverse_sort = false;
+ strncpy(newloc->lc_collate, locale, LOCALE_NAME_LEN);
+ }
+ if (category == LC_ALL || category == LC_CTYPE)
+ {
+ if (locale_is_reverse(locale))
+ newloc->reverse_case = true;
+ else
+ newloc->reverse_case = false;
+ strncpy(newloc->lc_ctype, locale, LOCALE_NAME_LEN);
+ }
+
+ return (locale_t) newloc;
+}
+#endif /* WIN32 */
+
+#endif /* HAVE_LOCALE_T */
+
+static size_t
+test_wcstombs(char *dest, const wchar_t *src, size_t n)
+{
+ return wcstombs(dest, src, n);
+}
+
+static size_t
+test_mbstowcs(wchar_t *dest, const char *src, size_t n)
+{
+ return mbstowcs(dest, src, n);
+}
+
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+static size_t
+test_wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
+{
+ return wcstombs(dest, src, n);
+}
+#endif
+#ifdef HAVE_MBSTOWCS_L
+static size_t
+test_mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
+{
+ return mbstowcs(dest, src, n);
+}
+#endif
+#endif
+
+static int
+test_strcoll_internal(const char *s1, const char *s2, bool reverse)
+{
+ int ret = strcmp(s1, s2);
+ return reverse ? -ret : ret;
+}
+
+static int
+test_strcoll(const char *s1, const char *s2)
+{
+ bool reverse = current_locale()->reverse_sort;
+ return test_strcoll_internal(s1, s2, reverse);
+}
+
+static int
+test_wcscoll_internal(const wchar_t *ws1, const wchar_t *ws2, bool reverse)
+{
+ int ret = wcscmp(ws1, ws2);
+ return reverse ? -ret : ret;
+}
+static int
+test_wcscoll(const wchar_t *ws1, const wchar_t *ws2)
+{
+ bool reverse = current_locale()->reverse_sort;
+ return test_wcscoll_internal(ws1, ws2, reverse);
+}
+
+static size_t
+test_strxfrm_internal(char *s1, const char *s2, size_t n, bool reverse)
+{
+ size_t result_size = strlen(s2) + 1;
+
+ if (n > result_size)
+ {
+ strncpy(s1, s2, n);
+ s1[result_size] = '\0';
+
+ if (reverse)
+ {
+ unsigned char *dest = (unsigned char *)s1;
+ for (int i = 0; i < result_size; i++)
+ dest[i] ^= (unsigned char) 0xFF;
+ }
+ }
+
+ return result_size;
+}
+
+static size_t
+test_strxfrm(char *s1, const char * s2, size_t n)
+{
+ bool reverse = current_locale()->reverse_sort;
+ return test_strxfrm_internal(s1, s2, n, reverse);
+}
+
+#ifdef HAVE_LOCALE_T
+static int
+test_strcoll_l(const char *s1, const char *s2, locale_t loc)
+{
+ test_locale_t *testlocale = (test_locale_t *)loc;
+ bool reverse = testlocale->reverse_sort;
+ return test_strcoll_internal(s1, s2, reverse);
+}
+
+static int
+test_wcscoll_l(const wchar_t *ws1, const wchar_t *ws2, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *) locale;
+ bool reverse = testlocale->reverse_sort;
+ return test_wcscoll_internal(ws1, ws2, reverse);
+}
+
+static size_t
+test_strxfrm_l(char *s1, const char * s2, size_t n, locale_t loc)
+{
+ test_locale_t *testlocale = (test_locale_t *) loc;
+ bool reverse = testlocale->reverse_sort;
+ return test_strxfrm_internal(s1, s2, n, reverse);
+}
+#endif /* HAVE_LOCALE_T */
+
+static int
+test_case_internal(int c, bool toupper)
+{
+ if (toupper && ('a' <= c && c <= 'z'))
+ return c - ('a' - 'A');
+ else if (!toupper && ('A' <= c && c <= 'Z'))
+ return c + ('a' - 'A');
+ else
+ return c;
+}
+
+static int
+test_tolower(int c)
+{
+ bool reverse = current_locale()->reverse_case;
+ return test_case_internal(c, reverse ? true : false);
+}
+
+static int
+test_toupper(int c)
+{
+ bool reverse = current_locale()->reverse_case;
+ return test_case_internal(c, reverse ? false : true);
+}
+
+static int
+test_iswalnum_internal(wint_t wc)
+{
+ if (('A' <= wc && wc <= 'Z') ||
+ ('a' <= wc && wc <= 'z') ||
+ ('0' <= wc && wc <= '9'))
+ return 1;
+ return 0;
+}
+
+static int
+test_iswalnum(wint_t wc)
+{
+ return test_iswalnum_internal(wc);
+}
+
+static wint_t
+test_wcase_internal(wint_t wc, bool toupper)
+{
+ if (toupper && ('a' <= wc && wc <= 'z'))
+ return wc - ('a' - 'A');
+ else if (!toupper && ('A' <= wc && wc <= 'Z'))
+ return wc + ('a' - 'A');
+ else
+ return wc;
+}
+
+static wint_t
+test_towlower(wint_t wc)
+{
+ bool reverse = current_locale()->reverse_case;
+ return test_wcase_internal(wc, reverse ? true : false);
+}
+
+static wint_t
+test_towupper(wint_t wc)
+{
+ bool reverse = current_locale()->reverse_case;
+ return test_wcase_internal(wc, reverse ? false : true);
+}
+
+#ifdef HAVE_LOCALE_T
+static int
+test_tolower_l(int c, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *) locale;
+ bool reverse = testlocale->reverse_case;
+ return test_case_internal(c, reverse ? true : false);
+}
+
+static int
+test_toupper_l(int c, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *) locale;
+ bool reverse = testlocale->reverse_case;
+ return test_case_internal(c, reverse ? false : true);
+}
+
+static int
+test_iswalnum_l(wint_t wc, locale_t locale)
+{
+ return test_iswalnum_internal(wc);
+}
+
+static wint_t
+test_towlower_l(wint_t wc, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *) locale;
+ bool reverse = testlocale->reverse_case;
+ return test_wcase_internal(wc, reverse ? true : false);
+}
+
+static wint_t
+test_towupper_l(wint_t wc, locale_t locale)
+{
+ test_locale_t *testlocale = (test_locale_t *) locale;
+ bool reverse = testlocale->reverse_case;
+ return test_wcase_internal(wc, reverse ? false : true);
+}
+#endif /* HAVE_LOCALE_T */
+
+pg_libc_library *
+test_get_libc_library(const char *collate, const char *ctype,
+ const char *version)
+{
+ pg_libc_library *lib = NULL;
+
+ if (test_libc_library != NULL)
+ return test_libc_library;
+
+ ereport(LOG, (errmsg("loading custom libc provider for test_collation_lib_hooks")));
+
+ lib = MemoryContextAllocZero(TopMemoryContext, sizeof(pg_libc_library));
+#if defined(__GLIBC__)
+ lib->libc_version = test_libc_version;
+#elif defined(WIN32)
+ lib->GetNLSVersionEx = test_GetNLSVersionEx;
+#endif
+ lib->c_setlocale = test_setlocale;
+#ifdef HAVE_LOCALE_T
+#ifndef WIN32
+ lib->c_newlocale = test_newlocale;
+ lib->c_freelocale = test_freelocale;
+ lib->c_uselocale = test_uselocale;
+#ifdef LC_VERSION_MASK
+ lib->c_querylocale = test_querylocale;
+#endif
+#else
+ lib->_create_locale = _test_create_locale;
+#endif
+#endif
+ lib->c_wcstombs = test_wcstombs;
+ lib->c_mbstowcs = test_mbstowcs;
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+ lib->c_wcstombs_l = test_wcstombs_l;
+#endif
+#ifdef HAVE_MBSTOWCS_L
+ lib->c_mbstowcs_l = test_mbstowcs_l;
+#endif
+#endif
+ lib->c_strcoll = test_strcoll;
+ lib->c_wcscoll = test_wcscoll;
+ lib->c_strxfrm = test_strxfrm;
+#ifdef HAVE_LOCALE_T
+ lib->c_strcoll_l = test_strcoll_l;
+ lib->c_wcscoll_l = test_wcscoll_l;
+ lib->c_strxfrm_l = test_strxfrm_l;
+#endif
+ lib->c_tolower = test_tolower;
+ lib->c_toupper = test_toupper;
+ lib->c_iswalnum = test_iswalnum;
+ lib->c_towlower = test_towlower;
+ lib->c_towupper = test_towupper;
+#ifdef HAVE_LOCALE_T
+ lib->c_tolower_l = test_tolower_l;
+ lib->c_toupper_l = test_toupper_l;
+ lib->c_iswalnum_l = test_iswalnum_l;
+ lib->c_towlower_l = test_towlower_l;
+ lib->c_towupper_l = test_towupper_l;
+#endif
+
+ test_libc_library = lib;
+ return lib;
+}
--
2.34.1