pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
WIN32 pg_import_system_collations
32+ messages / 8 participants
[nested] [flat]

Commits from this thread:
  2023-02-28 b5737efea0  Remove unnecessary and problematic collate.windows.win1252 tests
  2023-01-03 bf03cfd162  Windows support in pg_import_system_collations
* WIN32 pg_import_system_collations
@ 2021-12-13 08:41 Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2021-12-13 08:41 UTC (permalink / raw)
  To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

I want to propose an implementation of pg_import_system_collations() for
WIN32 using EnumSystemLocalesEx() [1], which is available from Windows
Server 2008 onwards.

The patch includes a test emulating that of collate.linux.utf8, but for
Windows-1252. The main difference is that it doesn't have the tests for
Turkish dotted and undotted 'i', since that locale is WIN1254.

I am opening an item in the commitfest for this.

[1]
https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-enumsystemlocalesex

Regards,

Juan José Santamaría Flecha

Attachments:

0001-WIN32-pg_import_system_collations.patch (application/x-patch, 52.0K) download From ddf3114e82dc32edb59278d9ed2cb995222a5adf Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Fri, 10 Dec 2021 17:44:15 -0500 Subject: [PATCH] WIN32 pg_import_system_collations . Adds the pg_import_system_collations() functionality for Windows newer than 2008 using EnumSystemLocalesEx(). Also adding a test emulating collate.linux.utf8 but for Windows-1252 --- src/backend/commands/collationcmds.c | 197 +++- .../regress/expected/collate.windows.win1252.out | 1019 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 12 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 418 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1599 insertions(+), 52 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 53fc579..c286113 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -522,6 +522,128 @@ get_icu_locale_comment(const char *localename) } #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#if defined(WIN32) && _WIN32_WINNT >= 0x0600 +#define ENUM_SYSTEM_LOCALE +#endif + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *localebuf, int *nvalid, int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(localebuf, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where ANSI + * uses an underscore. Simply make it ANSI looking. + */ + hyphen = strchr(localebuf, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(localebuf, param->nvalid, param->ncreated, param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + /* * pg_import_system_collations: add known system collations to pg_collation @@ -581,58 +703,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -702,6 +775,30 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* READ_LOCALE_A_OUTPUT */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + /* * Load collations known to ICU * diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..cc91c85 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1019 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like turkish dotted and undotted 'i' or greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7e45476 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,12 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like Turkish dotted and undotted 'i' or Greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 017e962..0e1c853 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -91,7 +91,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize misc_func # rules cannot run concurrently with any test that creates # a view or rule in the public schema # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # run by itself so it can run parallel workers test: select_parallel diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..e0e5cea --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,418 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like turkish dotted and undotted 'i' or greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 5975e7c..ea2add2 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -154,6 +154,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -169,7 +170,7 @@ sub check "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", "--make-testtablespace-dir", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2021-12-13 16:28 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 20:53   ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  2022-01-19 09:53   ` Re: WIN32 pg_import_system_collations Julien Rouhaud <rjuju123@gmail.com>
  0 siblings, 2 replies; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2021-12-13 16:28 UTC (permalink / raw)
  To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Dec 13, 2021 at 9:41 AM Juan José Santamaría Flecha <
juanjo.santamaria@gmail.com> wrote:

Per path tester.


> Regards,
>
> Juan José Santamaría Flecha
>

Attachments:

v2-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 52.0K) download From ddf3114e82dc32edb59278d9ed2cb995222a5adf Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Fri, 10 Dec 2021 17:44:15 -0500 Subject: [PATCH] WIN32 pg_import_system_collations . Adds the pg_import_system_collations() functionality for Windows newer than 2008 using EnumSystemLocalesEx(). Also adding a test emulating collate.linux.utf8 but for Windows-1252 --- src/backend/commands/collationcmds.c | 197 +++- .../regress/expected/collate.windows.win1252.out | 1019 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 12 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 418 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1599 insertions(+), 52 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 53fc579..c286113 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -522,6 +522,128 @@ get_icu_locale_comment(const char *localename) } #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#if defined(WIN32) && _WIN32_WINNT >= 0x0600 +#define ENUM_SYSTEM_LOCALE +#endif + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *localebuf, int *nvalid, int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(localebuf, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where ANSI + * uses an underscore. Simply make it ANSI looking. + */ + hyphen = strchr(localebuf, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(localebuf, param->nvalid, param->ncreated, param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + /* * pg_import_system_collations: add known system collations to pg_collation @@ -581,58 +703,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -702,6 +775,30 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* READ_LOCALE_A_OUTPUT */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + /* * Load collations known to ICU * diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..cc91c85 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1019 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like Turkish dotted and undotted 'i' or Greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7e45476 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,12 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like Turkish dotted and undotted 'i' or Greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 017e962..0e1c853 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -91,7 +91,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize misc_func # rules cannot run concurrently with any test that creates # a view or rule in the public schema # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # run by itself so it can run parallel workers test: select_parallel diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..e0e5cea --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,418 @@ +/* + * This test is for Windows/Visual Studio systems and assumes that a full set + * of locales is installed. It must be run in a database with WIN1252 encoding, + * because of the locales' encondings. We lose some interesting cases from the + * UTF-8 version, like Turkish dotted and undotted 'i' or Greek sigma. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 5975e7c..ea2add2 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -154,6 +154,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -169,7 +170,7 @@ sub check "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", "--make-testtablespace-dir", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2021-12-13 20:53   ` Thomas Munro <thomas.munro@gmail.com>
  2021-12-14 20:13     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  1 sibling, 1 reply; 32+ messages in thread

From: Thomas Munro @ 2021-12-13 20:53 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Dec 14, 2021 at 5:29 AM Juan José Santamaría Flecha
<juanjo.santamaria@gmail.com> wrote:
> On Mon, Dec 13, 2021 at 9:41 AM Juan José Santamaría Flecha <juanjo.santamaria@gmail.com> wrote:
> Per path tester.

Hi Juan José,

I haven't tested yet but +1 for the feature.  I guess the API didn't
exist at the time collation support was added.

+    /*
+     * Windows will use hyphens between language and territory, where ANSI
+     * uses an underscore. Simply make it ANSI looking.
+     */
+    hyphen = strchr(localebuf, '-');
+    if (hyphen)
+        *hyphen = '_';
+

This conversion makes sense, to keep the user experience the same
across platforms.   Nitpick on the comment: why ANSI?  I think we can
call "en_NZ" a POSIX locale identifier[1], and I think we can call
"en-NZ" a BCP 47 language tag.

+/*
+ * This test is for Windows/Visual Studio systems and assumes that a full set
+ * of locales is installed. It must be run in a database with WIN1252 encoding,
+ * because of the locales' encondings. We lose some interesting cases from the
+ * UTF-8 version, like Turkish dotted and undotted 'i' or Greek sigma.
+ */

s/encondings/encodings/

When would the full set of locales not be installed on a Windows
system, and why does this need Visual Studio?  Wondering if this test
will work with some of the frankenstein/cross toolchains tool chains
(not objecting if it doesn't and could be skipped, just trying to
understand the comment).

Slightly related to this, in case you didn't see it, I'd also like to
use BCP 47 tags for the default locale for PostgreSQL 15[2].

[1] https://en.wikipedia.org/wiki/Locale_(computer_software)#POSIX_platforms
[2] https://www.postgresql.org/message-id/flat/CA%2BhUKGJ%3DXThErgAQRoqfCy1bKPxXVuF0%3D2zDbB%2BSxDs59pv7...





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 20:53   ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
@ 2021-12-14 20:13     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-14 21:45       ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2021-12-14 20:13 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Dec 13, 2021 at 9:54 PM Thomas Munro <thomas.munro@gmail.com> wrote:

>
> I haven't tested yet but +1 for the feature.  I guess the API didn't
> exist at the time collation support was added.
>
> Good to hear.


> This conversion makes sense, to keep the user experience the same
> across platforms.   Nitpick on the comment: why ANSI?  I think we can
> call "en_NZ" a POSIX locale identifier[1], and I think we can call
> "en-NZ" a BCP 47 language tag.
>
> POSIX also works for me.


> When would the full set of locales not be installed on a Windows
> system, and why does this need Visual Studio?  Wondering if this test
> will work with some of the frankenstein/cross toolchains tool chains
> (not objecting if it doesn't and could be skipped, just trying to
> understand the comment).
>
> What I meant to say is that to run the test, you need a database that has
successfully run pg_import_system_collations. This would be also possible
in Mingw for _WIN32_WINNT> = 0x0600, but the current value in
src\include\port\win32.h is _WIN32_WINNT = 0x0501 when compiling with
Mingw.


> Regards,

 Juan José Santamaría Flecha

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 20:53   ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  2021-12-14 20:13     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2021-12-14 21:45       ` Thomas Munro <thomas.munro@gmail.com>
  2021-12-15 02:52         ` Re: WIN32 pg_import_system_collations Michael Paquier <michael@paquier.xyz>
  0 siblings, 1 reply; 32+ messages in thread

From: Thomas Munro @ 2021-12-14 21:45 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Dec 15, 2021 at 9:14 AM Juan José Santamaría Flecha
<juanjo.santamaria@gmail.com> wrote:
> What I meant to say is that to run the test, you need a database that has successfully run pg_import_system_collations. This would be also possible in Mingw for _WIN32_WINNT> = 0x0600, but the current value in src\include\port\win32.h is _WIN32_WINNT = 0x0501 when compiling with Mingw.

Ah, right.  I hope we can make the leap to 0x0A00 (Win10) soon and
just stop thinking about these old ghosts, as mentioned by various
people in various threads.  Do you happen to know if there are
complications for that, with the non-MSVC tool chains?





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 20:53   ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  2021-12-14 20:13     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-14 21:45       ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
@ 2021-12-15 02:52         ` Michael Paquier <michael@paquier.xyz>
  2021-12-15 04:03           ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Michael Paquier @ 2021-12-15 02:52 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Dec 15, 2021 at 10:45:28AM +1300, Thomas Munro wrote:
> Ah, right.  I hope we can make the leap to 0x0A00 (Win10) soon and
> just stop thinking about these old ghosts, as mentioned by various
> people in various threads.

Seeing your message here..  My apologies for the short digression.
Would that mean that we could use CreateSymbolicLinkA() as a mapper
for pgreadlink() rather than junction points?  I am wondering how much
code in src/port/ such a move could allow us to do.
--
Michael

Attachments:

  signature.asc (application/pgp-signature, 832B) download

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 20:53   ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  2021-12-14 20:13     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-14 21:45       ` Re: WIN32 pg_import_system_collations Thomas Munro <thomas.munro@gmail.com>
  2021-12-15 02:52         ` Re: WIN32 pg_import_system_collations Michael Paquier <michael@paquier.xyz>
@ 2021-12-15 04:03           ` Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 0 replies; 32+ messages in thread

From: Thomas Munro @ 2021-12-15 04:03 UTC (permalink / raw)
  To: Michael Paquier <michael@paquier.xyz>; +Cc: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Dec 15, 2021 at 3:52 PM Michael Paquier <michael@paquier.xyz> wrote:
> On Wed, Dec 15, 2021 at 10:45:28AM +1300, Thomas Munro wrote:
> > Ah, right.  I hope we can make the leap to 0x0A00 (Win10) soon and
> > just stop thinking about these old ghosts, as mentioned by various
> > people in various threads.
>
> Seeing your message here..  My apologies for the short digression.
> Would that mean that we could use CreateSymbolicLinkA() as a mapper
> for pgreadlink() rather than junction points?  I am wondering how much
> code in src/port/ such a move could allow us to do.

Sadly, (1) it wouldn't work unless running with a special privilege or
as admin, and (2) it wouldn't work on non-NTFS filesystems.  I think
it's mostly intended to allow things like unpacking tarballs, checking
out git repos etc etc etc that came from Unix systems, which is why it
works with 'developer mode' enabled[1], though obviously it wouldn't
be totally impossible for us to require that privilege.  Didn't seem
great to me, though, that's why I gave up on it over in
https://commitfest.postgresql.org/36/3090/ where this was recently
discussed.

[1] https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-01-19 09:53   ` Julien Rouhaud <rjuju123@gmail.com>
  2022-01-19 12:24     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  1 sibling, 1 reply; 32+ messages in thread

From: Julien Rouhaud @ 2022-01-19 09:53 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On Mon, Dec 13, 2021 at 05:28:47PM +0100, Juan José Santamaría Flecha wrote:
> On Mon, Dec 13, 2021 at 9:41 AM Juan José Santamaría Flecha <
> juanjo.santamaria@gmail.com> wrote:
> 
> Per path tester.

This version doesn't apply anymore:

http://cfbot.cputube.org/patch_36_3450.log
=== Applying patches on top of PostgreSQL commit ID e0e567a106726f6709601ee7cffe73eb6da8084e ===
=== applying patch ./v2-0001-WIN32-pg_import_system_collations.patch
[...]
patching file src/tools/msvc/vcregress.pl
Hunk #1 succeeded at 153 (offset -1 lines).
Hunk #2 FAILED at 170.
1 out of 2 hunks FAILED -- saving rejects to file src/tools/msvc/vcregress.pl.rej

Could you send a rebased version?  In the meantime I will switch the CF entry
to Waiting on Author.





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-01-19 09:53   ` Re: WIN32 pg_import_system_collations Julien Rouhaud <rjuju123@gmail.com>
@ 2022-01-19 12:24     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-01-25 07:56       ` Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-01-19 12:24 UTC (permalink / raw)
  To: Julien Rouhaud <rjuju123@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Jan 19, 2022 at 10:53 AM Julien Rouhaud <rjuju123@gmail.com> wrote:

>
> This version doesn't apply anymore:
>
> Thanks for the heads up.

Please find attached a rebased patch. I have also rewritten some comments
to address previous reviews, code and test remain the same.

Regards,

Juan José Santamaría Flecha

Attachments:

v3-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 52.2K) download From dd572aae14adfb262dc38bd0e011bf910cdd4f12 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Wed, 19 Jan 2022 07:15:15 -0500 Subject: [PATCH] WIN32 pg_import_system_collations . Adds the pg_import_system_collations() functionality for Windows newer than 2008 using EnumSystemLocalesEx(). Also adding a test emulating collate.linux.utf8 but for Windows-1252 --- src/backend/commands/collationcmds.c | 197 +++- .../regress/expected/collate.windows.win1252.out | 1020 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 419 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1602 insertions(+), 52 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 5674855..a1589c5 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -522,6 +522,128 @@ get_icu_locale_comment(const char *localename) } #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#if defined(WIN32) && _WIN32_WINNT >= 0x0600 +#define ENUM_SYSTEM_LOCALE +#endif + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *localebuf, int *nvalid, int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(localebuf, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply make it POSIX looking. + */ + hyphen = strchr(localebuf, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(localebuf, param->nvalid, param->ncreated, param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + /* * pg_import_system_collations: add known system collations to pg_collation @@ -581,58 +703,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -702,6 +775,30 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* READ_LOCALE_A_OUTPUT */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + /* * Load collations known to ICU * diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..1245c11 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1020 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7bc06ca --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,13 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5b0c73d..7eee69e 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -94,7 +94,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize misc_func # rules cannot run concurrently with any test that creates # a view or rule in the public schema # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # run by itself so it can run parallel workers test: select_parallel diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..81314d1 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,419 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 6dcd742..b6a3951 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -153,6 +153,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -167,7 +168,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2021-12-13 16:28 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-01-19 09:53   ` Re: WIN32 pg_import_system_collations Julien Rouhaud <rjuju123@gmail.com>
  2022-01-19 12:24     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-01-25 07:56       ` Dmitry Koval <d.koval@postgrespro.ru>
  0 siblings, 0 replies; 32+ messages in thread

From: Dmitry Koval @ 2022-01-25 07:56 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi Juan José,

I a bit tested this feature and have small doubts about block:

+/*
+ * Windows will use hyphens between language and territory, where POSIX
+ * uses an underscore. Simply make it POSIX looking.
+ */
+ hyphen = strchr(localebuf, '-');
+ if (hyphen)
+    *hyphen = '_';

After this block modified collation name is used in function

GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version)

(see win32_read_locale() -> CollationFromLocale() -> CollationCreate()
call). Is it correct to use (wide_collcollate = "en_NZ") instead of
(wide_collcollate = "en-NZ") in GetNLSVersionEx() function?

1) Documentation [1], [2], quote:
If it is a neutral locale for which the script is significant,
the pattern is <language>-<Script>.

2) Conversation [3], David Rowley, quote:
Then, since GetNLSVersionEx()
wants yet another variant with a - rather than an _, I've just added a
couple of lines to swap the _ for a -.


On my computer (Windows 10 Pro 21H2 19044.1466, MSVC2019 version
16.11.9) work correctly both variants ("en_NZ", "en-NZ").

But David Rowley (MSVC2010 and MSVC2017) replaced "_" to "-"
for the same function. Maybe he had a problem with "_" on MSVC2010 or 
MSVC2017?

[1] 
https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getnlsversionex
[2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
[3] 
https://www.postgresql.org/message-id/flat/CAApHDvq3FXpH268rt-6sD_Uhe7Ekv9RKXHFvpv%3D%3Duh4c9OeHHQ%4...

With best regards,
Dmitry Koval.





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
@ 2022-01-24 21:23 Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Dmitry Koval @ 2022-01-24 21:23 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi Juan José,

I a bit tested this feature and have small doubts about block:

+/*
+ * Windows will use hyphens between language and territory, where POSIX
+ * uses an underscore. Simply make it POSIX looking.
+ */
+ hyphen = strchr(localebuf, '-');
+ if (hyphen)
+    *hyphen = '_';

After this block modified collation name is used in function

GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version)

(see win32_read_locale() -> CollationFromLocale() -> CollationCreate()
call). Is it correct to use (wide_collcollate = "en_NZ") instead of
(wide_collcollate = "en-NZ") in GetNLSVersionEx() function?

1) Documentation [1], [2], quote:
If it is a neutral locale for which the script is significant,
the pattern is <language>-<Script>.

2) Conversation [3], David Rowley, quote:
Then, since GetNLSVersionEx()
wants yet another variant with a - rather than an _, I've just added a
couple of lines to swap the _ for a -.


On my computer (Windows 10 Pro 21H2 19044.1466, MSVC2019 version
16.11.9) work correctly both variants ("en_NZ", "en-NZ").

But David Rowley (MSVC2010 and MSVC2017) replaced "_" to "-"
for the same function. Maybe he had a problem with "_" on MSVC2010 or 
MSVC2017?

[1] 
https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getnlsversionex
[2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
[3] 
https://www.postgresql.org/message-id/flat/CAApHDvq3FXpH268rt-6sD_Uhe7Ekv9RKXHFvpv%3D%3Duh4c9OeHHQ%4...

With best regards,
Dmitry Koval.





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
@ 2022-01-25 10:40 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Peter Eisentraut @ 2022-01-25 10:40 UTC (permalink / raw)
  To: Dmitry Koval <d.koval@postgrespro.ru>; Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 24.01.22 22:23, Dmitry Koval wrote:
> +/*
> + * Windows will use hyphens between language and territory, where POSIX
> + * uses an underscore. Simply make it POSIX looking.
> + */
> + hyphen = strchr(localebuf, '-');
> + if (hyphen)
> +    *hyphen = '_';
> 
> After this block modified collation name is used in function
> 
> GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version)
> 
> (see win32_read_locale() -> CollationFromLocale() -> CollationCreate()
> call). Is it correct to use (wide_collcollate = "en_NZ") instead of
> (wide_collcollate = "en-NZ") in GetNLSVersionEx() function?

I don't really know if this is necessary anyway.  Just create the 
collations with the names that the operating system presents.  There is 
no requirement to make the names match POSIX.

If you want to make them match POSIX for some reason, you can also just 
change the object name but leave the collcollate/collctype fields the 
way they came from the OS.





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2022-01-25 14:49   ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-01-25 14:49 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Jan 25, 2022 at 11:40 AM Peter Eisentraut <
peter.eisentraut@enterprisedb.com> wrote:

> On 24.01.22 22:23, Dmitry Koval wrote:
>

Thanks for looking into this.


> > +/*
> > + * Windows will use hyphens between language and territory, where POSIX
> > + * uses an underscore. Simply make it POSIX looking.
> > + */
> > + hyphen = strchr(localebuf, '-');
> > + if (hyphen)
> > +    *hyphen = '_';
> >
> > After this block modified collation name is used in function
> >
> > GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version)
> >
> > (see win32_read_locale() -> CollationFromLocale() -> CollationCreate()
> > call). Is it correct to use (wide_collcollate = "en_NZ") instead of
> > (wide_collcollate = "en-NZ") in GetNLSVersionEx() function?
>

The problem that David Rowley addressed was coming from Windows collations
in the shape of "English_New Zealand", GetNLSVersionEx() will work with
both "en_NZ" and "en-NZ". You can check collversion in pg_collation in the
patched version.

>
> I don't really know if this is necessary anyway.  Just create the
> collations with the names that the operating system presents.  There is
> no requirement to make the names match POSIX.
>
> If you want to make them match POSIX for some reason, you can also just
> change the object name but leave the collcollate/collctype fields the
> way they came from the OS.
>

I think there is some value in making collation names consistent across
different platforms, e.g. making user scripts more portable. So, I'm doing
that in the attached version, just changing the object name.

Regards,

Juan José Santamaría Flecha

Attachments:

v4-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 52.3K) download From a9039ac6ceacbf86a39c135e9982d04d5610e1ef Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 25 Jan 2022 09:09:02 -0500 Subject: [PATCH] WIN32 pg_import_system_collations . Adds the pg_import_system_collations() functionality for Windows newer than 2008 using EnumSystemLocalesEx(). Also adding a test emulating collate.linux.utf8 but for Windows-1252 --- src/backend/commands/collationcmds.c | 201 +++- .../regress/expected/collate.windows.win1252.out | 1020 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 419 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1606 insertions(+), 52 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 5674855..ba40b4a 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -522,6 +522,132 @@ get_icu_locale_comment(const char *localename) } #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#if defined(WIN32) && _WIN32_WINNT >= 0x0600 +#define ENUM_SYSTEM_LOCALE +#endif + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *isolocale, char *localebuf, int *nvalid, + int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(isolocale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + char isolocale[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply make it POSIX looking. + */ + strcpy(isolocale, localebuf); + hyphen = strchr(isolocale, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(isolocale, localebuf, param->nvalid, param->ncreated, + param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + /* * pg_import_system_collations: add known system collations to pg_collation @@ -581,58 +707,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -702,6 +779,30 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* READ_LOCALE_A_OUTPUT */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + /* * Load collations known to ICU * diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..1245c11 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1020 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7bc06ca --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,13 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5b0c73d..7eee69e 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -94,7 +94,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize misc_func # rules cannot run concurrently with any test that creates # a view or rule in the public schema # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # run by itself so it can run parallel workers test: select_parallel diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..81314d1 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,419 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 6dcd742..b6a3951 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -153,6 +153,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -167,7 +168,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-03-22 01:00     ` Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Andres Freund @ 2022-03-22 01:00 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On 2022-01-25 15:49:01 +0100, Juan José Santamaría Flecha wrote:
> So, I'm doing that in the attached version, just changing the object name.

Currently fails to apply, please rebase: http://cfbot.cputube.org/patch_37_3450.log

Marked as waiting-on-author.

- Andres





^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
@ 2022-04-11 12:20       ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-04-11 12:20 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Mar 22, 2022 at 2:00 AM Andres Freund <andres@anarazel.de> wrote:

>
> Currently fails to apply, please rebase:
> http://cfbot.cputube.org/patch_37_3450.log
>
> Marked as waiting-on-author.
>
> Please, find attached a rebased version, no other significant change.

Regards,

Juan José Santamaría Flecha

Attachments:

v5-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 52.3K) download From 0fb80a45bb1f09dcf6799f053e3e03ba58150266 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Mon, 11 Apr 2022 07:27:33 -0400 Subject: [PATCH] WIN32 pg_import_system_collations . Adds the pg_import_system_collations() functionality for Windows newer than 2008 using EnumSystemLocalesEx(). Also adding a test emulating collate.linux.utf8 but for Windows-1252 --- src/backend/commands/collationcmds.c | 203 +++- .../regress/expected/collate.windows.win1252.out | 1020 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 419 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1608 insertions(+), 52 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 346f85f..c997edb 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -574,6 +574,134 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#if defined(WIN32) && _WIN32_WINNT >= 0x0600 +#define ENUM_SYSTEM_LOCALE +#endif + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *isolocale, char *localebuf, int *nvalid, + int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(isolocale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + char isolocale[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply make it POSIX looking. + */ + strcpy(isolocale, localebuf); + hyphen = strchr(isolocale, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(isolocale, localebuf, param->nvalid, param->ncreated, + param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -632,58 +760,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -815,5 +894,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..1245c11 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1020 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7bc06ca --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,13 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 103e114..9ea2e60 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -89,7 +89,7 @@ test: brin_bloom brin_multi test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # ---------- # Run these alone so they don't run out of parallel workers diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..81314d1 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,419 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index fc7aa8b..a1b081a 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -154,6 +154,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -168,7 +169,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-07-12 19:32         ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-07-12 19:32 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Please find attached a rebased version. I have split the patch into two
parts trying to make it easier to review, one with the code changes and the
other with the test.

Other than that, there are minimal changes from the previous version to the
code due to the update of _WIN32_WINNT and enabling the test on cirrus.

Regards,

Juan José Santamaría Flecha

>

Attachments: download all (2 files, .tar.gz)

v6-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 7.0K) download From 7fb63f46bf762b699ceb291c8f1f68a3bee17c18 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 12 Jul 2022 09:23:06 -0400 Subject: [PATCH] WIN32 pg_import_system_collations --- src/backend/commands/collationcmds.c | 204 ++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 50 deletions(-) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index fcfc02d..240fa50 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -575,6 +575,135 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#ifdef WIN32 +#define ENUM_SYSTEM_LOCALE +#endif + + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a collation if the input locale is valid for so. + * Also keeps track of the number of valid locales and collations created. + */ +static int +CollationFromLocale(char *isolocale, char *localebuf, int *nvalid, + int *ncreated, int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(localebuf)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); + return -1; + } + + enc = pg_get_encoding_from_locale(localebuf, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", + localebuf); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(isolocale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* Parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale. + */ +static BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + char isolocale[NAMEDATALEN]; + int result; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply make it POSIX looking. + */ + strcpy(isolocale, localebuf); + hyphen = strchr(isolocale, '-'); + if (hyphen) + *hyphen = '_'; + + CollationFromLocale(isolocale, localebuf, param->nvalid, param->ncreated, + param->nspid); + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -633,58 +762,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = CollationFromLocale(localebuf, localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -816,5 +896,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } -- 2.11.0
v6-0002-Add-collate.windows.win1252-test.patch (application/octet-stream, 45.9K) download From 16a1586c2123c66870fc8d41238696bca0742dc3 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 12 Jul 2022 10:58:07 -0400 Subject: [PATCH] Add collate.windows.win1252 test emulating collate.linux.utf8 --- .cirrus.yml | 3 + .../regress/expected/collate.windows.win1252.out | 1020 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 419 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1458 insertions(+), 2 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/.cirrus.yml b/.cirrus.yml index f23d6ca..54a36e0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -386,6 +386,9 @@ task: # currently have a tool for that... CIRRUS_ESCAPING_PROCESSES: 1 + # Enable locale testing by importing system locales + ENCODING: "WIN1252" + only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\nci-os-only:.*' || $CIRRUS_CHANGE_MESSAGE =~ '.*\nci-os-only:[^\n]*windows.*' windows_container: diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..1245c11 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1020 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7bc06ca --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,13 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 103e114..9ea2e60 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -89,7 +89,7 @@ test: brin_bloom brin_multi test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # ---------- # Run these alone so they don't run out of parallel workers diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..81314d1 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,419 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index c3729f6..113bf18 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -184,6 +184,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -198,7 +199,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-10-31 14:09           ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Peter Eisentraut @ 2022-10-31 14:09 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 12.07.22 21:32, Juan José Santamaría Flecha wrote:
> Please find attached a rebased version. I have split the patch into two 
> parts trying to make it easier to review, one with the code changes and 
> the other with the test.
> 
> Other than that, there are minimal changes from the previous version to 
> the code due to the update of _WIN32_WINNT and enabling the test on cirrus.

I'm not familiar with Windows, so I'm just looking at the overall 
structure of this patch.  I think it pretty much makes sense.  But we 
need to consider that this operates on the confluence of various 
different operating system interfaces that not all people will be 
familiar with, so we need to really get the documentation done well.

Consider this function you are introducing:

+/*
+ * Create a collation if the input locale is valid for so.
+ * Also keeps track of the number of valid locales and collations created.
+ */
+static int
+CollationFromLocale(char *isolocale, char *localebuf, int *nvalid,
+                                       int *ncreated, int nspid)

This declaration is incomprehensible without studying all the callers 
and the surrounding code.

Start with the name: What does "collation from locale" mean?  Does it 
make a collation?  Does it convert one?  Does it find one?  There should 
be a verb in there.

(I think in the context of this file, a lower case name would be more 
appropriate for a static function.)

Then the arguments.  The input arguments should be "const".  All the 
arguments should be documented.  What is "isolocale", what is 
"localebuf", how are they different?  What is being counted by "valid" 
(collatons?, locales?), and what makes a thing valid and invalid?  What 
is being "created"?  What is nspid?  What is the return value?

Please make another pass over this.

Also consider describing in the commit message what you are doing in 
more detail, including some of the things that have been discussed in 
this thread.






^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2022-11-04 22:08             ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-11-04 22:08 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Oct 31, 2022 at 3:09 PM Peter Eisentraut <
peter.eisentraut@enterprisedb.com> wrote:

Thanks for taking a look into this patch.

>
> Consider this function you are introducing:
>
> +/*
> + * Create a collation if the input locale is valid for so.
> + * Also keeps track of the number of valid locales and collations created.
> + */
> +static int
> +CollationFromLocale(char *isolocale, char *localebuf, int *nvalid,
> +                                       int *ncreated, int nspid)
>
> This declaration is incomprehensible without studying all the callers
> and the surrounding code.
>
> Start with the name: What does "collation from locale" mean?  Does it
> make a collation?  Does it convert one?  Does it find one?  There should
> be a verb in there.
>
> (I think in the context of this file, a lower case name would be more
> appropriate for a static function.)
>
> Then the arguments.  The input arguments should be "const".  All the
> arguments should be documented.  What is "isolocale", what is
> "localebuf", how are they different?  What is being counted by "valid"
> (collatons?, locales?), and what makes a thing valid and invalid?  What
> is being "created"?  What is nspid?  What is the return value?
>
> Please make another pass over this.
>
> Ok, I can definitely improve the comments for that function.


> Also consider describing in the commit message what you are doing in
> more detail, including some of the things that have been discussed in
> this thread.
>
> Going through the thread for the commit message, I think that maybe the
collation naming remarks were not properly addressed. In the current
version the collations retain their native name, but an alias is created
for those with a shape that we can assume a POSIX equivalent exists.

Please find attached a new version.

Regards,

Juan José Santamaría Flecha

Attachments: download all (2 files, .tar.gz)

v7-0002-Add-collate.windows.win1252-test.patch (application/octet-stream, 45.7K) download From ce501c8640c18f6aced9d1197f77be3b89d1f243 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Thu, 3 Nov 2022 10:45:38 -0400 Subject: [PATCH] Add collate.windows.win1252 test emulating collate.linux.utf8 --- .cirrus.yml | 3 + .../regress/expected/collate.windows.win1252.out | 1020 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 13 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 419 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1458 insertions(+), 2 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/.cirrus.yml b/.cirrus.yml index 9f22824..3815679 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -428,6 +428,9 @@ task: # 0x8001 is SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX CIRRUS_WINDOWS_ERROR_MODE: 0x8001 + # Enable locale testing by importing system locales + ENCODING: "WIN1252" + only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\nci-os-only:.*' || $CIRRUS_CHANGE_MESSAGE =~ '.*\nci-os-only:[^\n]*windows.*' windows_container: diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..04e3573 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1020 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..7bc06ca --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,13 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 9a139f1..f99e993 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -89,7 +89,7 @@ test: brin_bloom brin_multi test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # ---------- # Run these alone so they don't run out of parallel workers diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..81314d1 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,419 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). This is possible for builds with a minimum + * system version of Windows Vista, and onwards. Also, the database must have + * WIN1252 encoding, because of the locales' own encodings. Due to this, some + * test are lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + version() !~ 'Visual C\+\+' + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 1d86cd6..823fea0 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -185,6 +185,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -199,7 +200,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
v7-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 8.4K) download From 7f5840684e6f34f2aeb7e062d9e6bf7dc7217ddc Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Fri, 4 Nov 2022 18:02:17 -0400 Subject: [PATCH] WIN32 pg_import_system_collations Windows can enumerate the locales that are either installed or supported by calling EnumSystemLocalesEx(), similar to what is already done in the READ_LOCALE_A_OUTPUT switch. We can refactor some of the logic already used in that switch into a new function create_collation_from_locale(). The enumerated locales have BCP 47 shape, that is with a hyphen between language and territory, instead of POSIX's underscore. The created collations will retain that BCP 47 shape, but we will also create a POSIX alias when we can assume that is sane, so xx-YY will have an xx_YY alias. --- src/backend/commands/collationcmds.c | 230 +++++++++++++++++++++++++++-------- 1 file changed, 180 insertions(+), 50 deletions(-) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 86fbc7f..e5208ed 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -609,6 +609,161 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#ifdef WIN32 +#define ENUM_SYSTEM_LOCALE +#endif + + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a new collation using the input locale 'locale'. + * + * The parameter 'nvalid' is incremented if the locale has a valid encoding. + * + * The parameter 'ncreated' is incremented if the collation is actually + * created, if the collation already exists it will quietly do nothing. + * + * The parameter 'nspid' is the namespace id where the collation will be + * created. + * + * The returned value is the encoding of the locale, -1 if it is not valid for + * creating a collation. + */ +static int +create_collation_from_locale(const char *locale, int *nvalid, int *ncreated, + const int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(locale)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", locale); + return -1; + } + + enc = pg_get_encoding_from_locale(locale, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", locale); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", locale); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + *nvalid += 1; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(locale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + locale, locale, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, locale), + true, true); + if (OidIsValid(collid)) + { + *ncreated += 1; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale and a POSIX alias collation. + */ +static BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + int enc; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return (TRUE); + } + if (localebuf[0] == '\0') + return (TRUE); + + enc = create_collation_from_locale(localebuf, param->nvalid, param->ncreated, + param->nspid); + if (enc < 0) + return (TRUE); + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Create a POSIX alias for locales that is safe to assume + * that they actually exist, e.g. en-US as en_US. + */ + if (strlen(localebuf) == 5 && localebuf[2] == '-') + { + char alias[NAMEDATALEN]; + Oid collid; + + strcpy(alias, localebuf); + alias[2] = '_'; + collid = CollationCreate(alias, param->nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + *param->ncreated += 1; + + CommandCounterIncrement(); + } + } + + return (TRUE); +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -667,58 +822,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = create_collation_from_locale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); - continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -850,5 +956,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-11-07 15:08               ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Peter Eisentraut @ 2022-11-07 15:08 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 04.11.22 23:08, Juan José Santamaría Flecha wrote:
> Ok, I can definitely improve the comments for that function.
> 
>     Also consider describing in the commit message what you are doing in
>     more detail, including some of the things that have been discussed in
>     this thread.
> 
> Going through the thread for the commit message, I think that maybe the 
> collation naming remarks were not properly addressed. In the current 
> version the collations retain their native name, but an alias is created 
> for those with a shape that we can assume a POSIX equivalent exists.

This looks pretty good to me.  The refactoring of the non-Windows parts 
makes sense.  The Windows parts look reasonable on manual inspection, 
but again, I don't have access to Windows here, so someone else should 
also look it over.

A small style issue: Change return (TRUE) to return TRUE.

The code

+   if (strlen(localebuf) == 5 && localebuf[2] == '-')

might be too specific.  At least on some POSIX systems, I have seen 
locales with a three-letter language name.  Maybe you should look with 
strchr() and not be too strict about the exact position.

For the test patch, why is a separate test for non-UTF8 needed on 
Windows.  Does the UTF8 one not work?

+       version() !~ 'Visual C\+\+'

This probably won't work for MinGW.






^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2022-11-08 23:02                 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-11-08 23:02 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Nov 7, 2022 at 4:08 PM Peter Eisentraut <
peter.eisentraut@enterprisedb.com> wrote:

>
> This looks pretty good to me.  The refactoring of the non-Windows parts
> makes sense.  The Windows parts look reasonable on manual inspection,
> but again, I don't have access to Windows here, so someone else should
> also look it over.
>
> I was going to say that at least it is getting tested on the CI, but I
have found out that meson changes version(). That is fixed in this version.


> A small style issue: Change return (TRUE) to return TRUE.
>
> Fixed.


> The code
>
> +   if (strlen(localebuf) == 5 && localebuf[2] == '-')
>
> might be too specific.  At least on some POSIX systems, I have seen
> locales with a three-letter language name.  Maybe you should look with
> strchr() and not be too strict about the exact position.
>
> Ok, in this version the POSIX alias is created unconditionally.


> For the test patch, why is a separate test for non-UTF8 needed on
> Windows.  Does the UTF8 one not work?
>
> Windows locales will retain their CP_ACP encoding unless you change the OS
code page to UFT8, which is still experimental [1].


> +       version() !~ 'Visual C\+\+'
>
> This probably won't work for MinGW.
>
> When I proposed this patch it wouldn't have worked because of the
project's Windows minimum version requirement, now it should work in MinGW.
It actually doesn't because most locales are failing with "skipping locale
with unrecognized encoding", but checking what's wrong
with pg_get_encoding_from_locale() in MiNGW is subject for another thread.

[1]
https://stackoverflow.com/questions/56419639/what-does-beta-use-unicode-utf-8-for-worldwide-language...

Regards,

Juan José Santamaría Flecha

Attachments: download all (2 files, .tar.gz)

v8-0001-WIN32-pg_import_system_collations.patch (application/octet-stream, 8.4K) download From 300ce308d915e0f7f599d7090787a9266b87e49f Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 8 Nov 2022 05:54:33 -0500 Subject: [PATCH] WIN32 pg_import_system_collations Windows can enumerate the locales that are either installed or supported by calling EnumSystemLocalesEx(), similar to what is already done in the READ_LOCALE_A_OUTPUT switch. We can refactor some of the logic already used in that switch into a new function create_collation_from_locale(). The enumerated locales have BCP 47 shape, that is with a hyphen between language and territory, instead of POSIX's underscore. The created collations will retain the BCP 47 shape, but we will also create a POSIX alias, so xx-YY will have an xx_YY alias. --- src/backend/commands/collationcmds.c | 236 +++++++++++++++++++++++++++-------- 1 file changed, 186 insertions(+), 50 deletions(-) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 86fbc7f..d345454 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -609,6 +609,167 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#ifdef WIN32 +#define ENUM_SYSTEM_LOCALE +#endif + + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a new collation using the input locale 'locale'. + * + * The parameter 'nvalid' is incremented if the locale has a valid encoding. + * + * The parameter 'ncreated' is incremented if the collation is actually + * created, if the collation already exists it will quietly do nothing. + * + * The parameter 'nspid' is the namespace id where the collation will be + * created. + * + * The returned value is the encoding of the locale, -1 if it is not valid for + * creating a collation. + * + */ +static int +create_collation_from_locale(const char *locale, int *nvalid, int *ncreated, + const int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(locale)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", locale); + return -1; + } + + enc = pg_get_encoding_from_locale(locale, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", locale); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", locale); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + (*nvalid)++; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(locale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + locale, locale, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, locale), + true, true); + if (OidIsValid(collid)) + { + (*ncreated)++; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale and a POSIX alias collation. + */ +static BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + int enc; + char alias[NAMEDATALEN]; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return TRUE; + } + if (localebuf[0] == '\0') + return TRUE; + + enc = create_collation_from_locale(localebuf, param->nvalid, param->ncreated, + param->nspid); + if (enc < 0) + return TRUE; + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply create a POSIX alias. + */ + strcpy(alias, localebuf); + hyphen = strchr(alias, '-'); + if (hyphen) + { + Oid collid; + + while (hyphen) + { + *hyphen = '_'; + hyphen = strchr(hyphen, '-'); + } + collid = CollationCreate(alias, param->nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + (*param->ncreated)++; + + CommandCounterIncrement(); + } + } + + return TRUE; +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -667,58 +828,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = create_collation_from_locale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -850,5 +962,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } -- 2.11.0
v8-0002-Add-collate.windows.win1252-test.patch (application/octet-stream, 45.6K) download From e0c10b253a524e79eff7e407dcaff8a5edd86bb1 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 8 Nov 2022 14:45:03 -0500 Subject: [PATCH] Add collate.windows.win1252 test emulating collate.linux.utf8 --- .cirrus.yml | 3 + .../regress/expected/collate.windows.win1252.out | 1019 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 12 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 418 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 6 files changed, 1455 insertions(+), 2 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/.cirrus.yml b/.cirrus.yml index 9f22824..3815679 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -428,6 +428,9 @@ task: # 0x8001 is SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX CIRRUS_WINDOWS_ERROR_MODE: 0x8001 + # Enable locale testing by importing system locales + ENCODING: "WIN1252" + only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\nci-os-only:.*' || $CIRRUS_CHANGE_MESSAGE =~ '.*\nci-os-only:[^\n]*windows.*' windows_container: diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..25de369 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1019 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'msvc' AND version() !~ 'mingw32') + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..f68a6be --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,12 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'msvc' AND version() !~ 'mingw32') + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 9a139f1..f99e993 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -89,7 +89,7 @@ test: brin_bloom brin_multi test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # ---------- # Run these alone so they don't run out of parallel workers diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..aea2977 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,418 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'msvc' AND version() !~ 'mingw32') + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 1d86cd6..823fea0 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -185,6 +185,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -199,7 +200,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-11-10 10:08                   ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-11-10 10:08 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Nov 9, 2022 at 12:02 AM Juan José Santamaría Flecha <
juanjo.santamaria@gmail.com> wrote:

> On Mon, Nov 7, 2022 at 4:08 PM Peter Eisentraut <
> peter.eisentraut@enterprisedb.com> wrote:
>
>>
>> This looks pretty good to me.  The refactoring of the non-Windows parts
>> makes sense.  The Windows parts look reasonable on manual inspection,
>> but again, I don't have access to Windows here, so someone else should
>> also look it over.
>>
>> I was going to say that at least it is getting tested on the CI, but I
> have found out that meson changes version(). That is fixed in this version.
>

Now is currently failing due to [1], so maybe we can leave this patch on
hold until that's addressed.

[1]
https://www.postgresql.org/message-id/CAC%2BAXB1wJEqfKCuVcNpoH%3Dgxd61N%3D7c2fR3Ew6YRPpSfEUA%3DyQ%40...


Regards,

Juan José Santamaría Flecha

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2022-12-01 07:46                     ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Peter Eisentraut @ 2022-12-01 07:46 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 10.11.22 11:08, Juan José Santamaría Flecha wrote:
>         This looks pretty good to me.  The refactoring of the
>         non-Windows parts
>         makes sense.  The Windows parts look reasonable on manual
>         inspection,
>         but again, I don't have access to Windows here, so someone else
>         should
>         also look it over.
> 
>     I was going to say that at least it is getting tested on the CI, but
>     I have found out that meson changes version(). That is fixed in this
>     version.
> 
> 
> Now is currently failing due to [1], so maybe we can leave this patch on 
> hold until that's addressed.
> 
> [1] 
> https://www.postgresql.org/message-id/CAC%2BAXB1wJEqfKCuVcNpoH%3Dgxd61N%3D7c2fR3Ew6YRPpSfEUA%3DyQ%40... <https://www.postgresql.org/message-id/CAC%2BAXB1wJEqfKCuVcNpoH%3Dgxd61N%3D7c2fR3Ew6YRPpSfEUA%3DyQ%40...;

What is the status of this now?  I think the other issue has been addressed?






^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2022-12-09 12:48                       ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2022-12-09 12:48 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Dec 1, 2022 at 8:46 AM Peter Eisentraut <
peter.eisentraut@enterprisedb.com> wrote:

>
> What is the status of this now?  I think the other issue has been
> addressed?
>

Yes, that's addressed for MSVC builds. I think there are a couple of
pending issues for MinGW, but those should have their own threads.

The patch had rotten, so PFA a rebased version.

Regards,

Juan José Santamaría Flecha

Attachments: download all (2 files, .tar.gz)

v9-0002-Add-collate.windows.win1252-test.patch (application/x-patch, 45.1K) download From e28a8094ce6bd3a059bd93c2063890cccc23514f Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Thu, 1 Dec 2022 10:21:41 -0500 Subject: [PATCH 2/2] Add collate.windows.win1252 test emulating collate.linux.utf8 --- .../regress/expected/collate.windows.win1252.out | 1019 ++++++++++++++++++++ .../regress/expected/collate.windows.win1252_1.out | 12 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 418 ++++++++ src/tools/msvc/vcregress.pl | 3 +- 5 files changed, 1452 insertions(+), 2 deletions(-) create mode 100644 src/test/regress/expected/collate.windows.win1252.out create mode 100644 src/test/regress/expected/collate.windows.win1252_1.out create mode 100644 src/test/regress/sql/collate.windows.win1252.sql diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out new file mode 100644 index 0000000..3fbc5d8 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -0,0 +1,1019 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'mingw32' AND version() !~ 'windows') + AS skip_test \gset +\if :skip_test +\quit +\endif +SET client_encoding TO WIN1252; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for encoding "WIN1252" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en_US | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + false +------- + f +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); + to_char +------------- + 01 MRZ 2010 +(1 row) + +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + to_char +------------- + 01 MRZ 2010 +(1 row) + +-- to_date +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +ERROR: invalid value "MÄR" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +ERROR: invalid value "Mär" for "MON" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail +ERROR: invalid value "1234567890ab" for "MONTH" +DETAIL: The given value did not match any of the allowed values for this field. +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+--------------------------------------------------------- + collview1 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, + + | collate_test1.b + + | FROM collate_test1 + + | ORDER BY (collate_test1.b COLLATE "C"); +(2 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en_US" in non-recursive term but collation "de_DE" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f +(16 rows) + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f +(16 rows) + +-- collation override in plpgsql +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + t | f +---+--- + t | f +(1 row) + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; +SELECT mylt2('a', 'B') as f; + f +--- + f +(1 row) + +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN +SELECT mylt2('a', 'B' collate "POSIX") as f; + f +--- + f +(1 row) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; + a | dup +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + a | dup +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... + ^ +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + relname | pg_get_indexdef +--------------------+------------------------------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") + collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") +(4 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +ERROR: collation "test0" already exists +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +NOTICE: collation "test0" already exists, skipping +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +NOTICE: collation "test0" for encoding "WIN1252" already exists, skipping +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION testx (locale = 'nonsense'); -- fail +ERROR: could not create locale "nonsense": No such file or directory +DETAIL: The operating system could not find any locale data for the locale name "nonsense". +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for encoding "WIN1252" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname +---------- + test0 + test1 + test5 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for encoding "WIN1252" already exists in schema "collate_tests" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for encoding "WIN1252" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+---------------+----------------- + test0 | collate_tests | US English + test11 | test_schema | + test5 | collate_tests | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for encoding "WIN1252" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- ALTER +ALTER COLLATION "en_US" REFRESH VERSION; +NOTICE: version has not changed +-- dependencies +CREATE COLLATION test0 FROM "C"; +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: column b of table collate_dep_test1 depends on collation test0 +type collate_dep_dom1 depends on collation test0 +column y of composite type collate_dep_test2 depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to column b of table collate_dep_test1 +drop cascades to type collate_dep_dom1 +drop cascades to column y of composite type collate_dep_test2 +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 + Table "collate_tests.collate_dep_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + +\d collate_dep_test2 + Composite type "collate_tests.collate_dep_test2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + x | integer | | | + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; +-- test range types and collations +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); +select textrange_c('A','Z') @> 'b'::text; + ?column? +---------- + f +(1 row) + +select textrange_en_us('A','Z') @> 'b'::text; + ?column? +---------- + t +(1 row) + +drop type textrange_c; +drop type textrange_en_us; +-- nondeterministic collations +-- (not supported with libc provider) +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); +ERROR: nondeterministic collations not supported with this provider +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/test/regress/expected/collate.windows.win1252_1.out b/src/test/regress/expected/collate.windows.win1252_1.out new file mode 100644 index 0000000..2fb7c10 --- /dev/null +++ b/src/test/regress/expected/collate.windows.win1252_1.out @@ -0,0 +1,12 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'mingw32' AND version() !~ 'windows') + AS skip_test \gset +\if :skip_test +\quit diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 9a139f1..f99e993 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -89,7 +89,7 @@ test: brin_bloom brin_multi test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role # collate.*.utf8 tests cannot be run in parallel with each other -test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 +test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 # ---------- # Run these alone so they don't run out of parallel workers diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql new file mode 100644 index 0000000..b4487b0 --- /dev/null +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -0,0 +1,418 @@ +/* + * This test is meant to run on Windows systems that has successfully run + * pg_import_system_collations(). Also, the database must have WIN1252 + * encoding, because of the locales' own encodings. Due to this, some test are + * lost from UTF-8 version, like Turkish dotted and undotted 'i'. + */ +SELECT getdatabaseencoding() <> 'WIN1252' OR + (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR + (version() !~ 'Visual C\+\+' AND version() !~ 'mingw32' AND version() !~ 'windows') + AS skip_test \gset +\if :skip_test +\quit +\endif + +SET client_encoding TO WIN1252; + +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; +SELECT 'bbc' COLLATE "sv_SE" > 'äbc' COLLATE "sv_SE" AS "false"; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + +-- regular expressions + +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; +SELECT * FROM collate_test1 WHERE b ~ '^abc'; +SELECT * FROM collate_test1 WHERE b ~ 'bc'; +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; +SELECT * FROM collate_test1 WHERE b ~* '^abc'; +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en_US" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + + +-- to_char + +SET lc_time TO 'de_DE'; +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); + +-- to_date + +SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); +SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); +SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression types + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail + +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de_DE" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too + +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; + +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; + +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + +SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; + + +-- collation override in plpgsql + +CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; + +CREATE OR REPLACE FUNCTION + mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ +declare + xx text COLLATE "POSIX" := x; + yy text := y; +begin + return xx < yy; +end +$$; + +SELECT mylt2('a', 'B') as f; +SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations +SELECT mylt2('a', 'B' collate "POSIX") as f; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (anyelement) RETURNS anyelement + AS 'select $1' LANGUAGE sql; + +SELECT a, dup(b) FROM collate_test1 ORDER BY 2; +SELECT a, dup(b) FROM collate_test2 ORDER BY 2; +SELECT a, dup(b) FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); + +CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +-- We need to do this this way to cope with varying names for encodings: +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test0 (locale = ' || + quote_literal(current_setting('lc_collate')) || ');'; +END +$$; +CREATE COLLATION test0 FROM "C"; -- fail, duplicate name +CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped +CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped +do $$ +BEGIN + EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || + quote_literal(current_setting('lc_collate')) || + ', lc_ctype = ' || + quote_literal(current_setting('lc_ctype')) || ');'; +END +$$; +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype +CREATE COLLATION testx (locale = 'nonsense'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- ALTER + +ALTER COLLATION "en_US" REFRESH VERSION; + + +-- dependencies + +CREATE COLLATION test0 FROM "C"; + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; + +-- test range types and collations + +create type textrange_c as range(subtype=text, collation="C"); +create type textrange_en_us as range(subtype=text, collation="en_US"); + +select textrange_c('A','Z') @> 'b'::text; +select textrange_en_us('A','Z') @> 'b'::text; + +drop type textrange_c; +drop type textrange_en_us; + + +-- nondeterministic collations +-- (not supported with libc provider) + +CREATE COLLATION ctest_det (locale = 'en_US', deterministic = true); +CREATE COLLATION ctest_nondet (locale = 'en_US', deterministic = false); + + +-- cleanup +SET client_min_messages TO warning; +DROP SCHEMA collate_tests CASCADE; diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index 1d86cd6..823fea0 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -185,6 +185,7 @@ sub installcheck sub check { my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; # for backwards compatibility, "serial" runs the tests in # parallel_schedule one by one. my $maxconn = $maxconn; @@ -199,7 +200,7 @@ sub check "--bindir=", "--schedule=${schedule}_schedule", "--max-concurrent-tests=20", - "--encoding=SQL_ASCII", + "--encoding=${encoding}", "--no-locale", "--temp-instance=./tmp_check"); push(@args, $maxconn) if $maxconn; -- 2.11.0
v9-0001-WIN32-pg_import_system_collations.patch (application/x-patch, 8.4K) download From 4d334e48e468a05f1ef3136ce51c7f7668277026 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Thu, 1 Dec 2022 09:58:29 -0500 Subject: [PATCH 1/2] WIN32 pg_import_system_collations Windows can enumerate the locales that are either installed or supported by calling EnumSystemLocalesEx(), similar to what is already done in the READ_LOCALE_A_OUTPUT switch. We can refactor some of the logic already used in that switch into a new function create_collation_from_locale(). The enumerated locales have BCP 47 shape, that is with a hyphen between language and territory, instead of POSIX's underscore. The created collations will retain the BCP 47 shape, but we will also create a POSIX alias, so xx-YY will have an xx_YY alias. --- src/backend/commands/collationcmds.c | 236 +++++++++++++++++++++++++++-------- 1 file changed, 186 insertions(+), 50 deletions(-) diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 81e54e0..df972bd 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -610,6 +610,167 @@ get_icu_locale_comment(const char *localename) #endif /* USE_ICU */ +/* will we use EnumSystemLocalesEx in pg_import_system_collations? */ +#ifdef WIN32 +#define ENUM_SYSTEM_LOCALE +#endif + + +#if defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) +/* + * Create a new collation using the input locale 'locale'. + * + * The parameter 'nvalid' is incremented if the locale has a valid encoding. + * + * The parameter 'ncreated' is incremented if the collation is actually + * created, if the collation already exists it will quietly do nothing. + * + * The parameter 'nspid' is the namespace id where the collation will be + * created. + * + * The returned value is the encoding of the locale, -1 if it is not valid for + * creating a collation. + * + */ +static int +create_collation_from_locale(const char *locale, int *nvalid, int *ncreated, + const int nspid) +{ + int enc; + Oid collid; + + /* + * Some systems have locale names that don't consist entirely of + * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). + * This is pretty silly, since we need the locale itself to + * interpret the non-ASCII characters. We can't do much with + * those, so we filter them out. + */ + if (!pg_is_ascii(locale)) + { + elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", locale); + return -1; + } + + enc = pg_get_encoding_from_locale(locale, false); + if (enc < 0) + { + elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", locale); + return -1; + } + if (!PG_VALID_BE_ENCODING(enc)) + { + elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", locale); + return -1; + } + if (enc == PG_SQL_ASCII) + return -1; /* C/POSIX are already in the catalog */ + + /* count valid locales found in operating system */ + (*nvalid)++; + + /* + * Create a collation named the same as the locale, but quietly + * doing nothing if it already exists. This is the behavior we + * need even at initdb time, because some versions of "locale -a" + * can report the same locale name more than once. And it's + * convenient for later import runs, too, since you just about + * always want to add on new locales without a lot of chatter + * about existing ones. + */ + collid = CollationCreate(locale, nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + locale, locale, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, locale), + true, true); + if (OidIsValid(collid)) + { + (*ncreated)++; + + /* Must do CCI between inserts to handle duplicates correctly */ + CommandCounterIncrement(); + } + + return enc; +} +#endif /* defined(READ_LOCALE_A_OUTPUT) || defined(ENUM_SYSTEM_LOCALE) */ + + +#ifdef ENUM_SYSTEM_LOCALE +/* parameter to be passed to the callback function win32_read_locale() */ +typedef struct +{ + int *ncreated; + int *nvalid; + Oid nspid; +} CollParam; + +/* + * Callback function for EnumSystemLocalesEx() in pg_import_system_collations() + * Creates a collation for every valid locale and a POSIX alias collation. + */ +static BOOL CALLBACK +win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam) +{ + CollParam *param = (CollParam *) lparam; + char localebuf[NAMEDATALEN]; + int result; + int enc; + char alias[NAMEDATALEN]; + char *hyphen; + + (void) dwFlags; + + result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN, + NULL, NULL); + + if (result == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf); + return TRUE; + } + if (localebuf[0] == '\0') + return TRUE; + + enc = create_collation_from_locale(localebuf, param->nvalid, param->ncreated, + param->nspid); + if (enc < 0) + return TRUE; + + /* + * Windows will use hyphens between language and territory, where POSIX + * uses an underscore. Simply create a POSIX alias. + */ + strcpy(alias, localebuf); + hyphen = strchr(alias, '-'); + if (hyphen) + { + Oid collid; + + while (hyphen) + { + *hyphen = '_'; + hyphen = strchr(hyphen, '-'); + } + collid = CollationCreate(alias, param->nspid, GetUserId(), + COLLPROVIDER_LIBC, true, enc, + localebuf, localebuf, NULL, + get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), + true, true); + if (OidIsValid(collid)) + { + (*param->ncreated)++; + + CommandCounterIncrement(); + } + } + + return TRUE; +} +#endif /* ENUM_SYSTEM_LOCALE */ + + /* * pg_import_system_collations: add known system collations to pg_collation */ @@ -668,58 +829,9 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } localebuf[len - 1] = '\0'; - /* - * Some systems have locale names that don't consist entirely of - * ASCII letters (such as "bokm&aring;l" or "fran&ccedil;ais"). - * This is pretty silly, since we need the locale itself to - * interpret the non-ASCII characters. We can't do much with - * those, so we filter them out. - */ - if (!pg_is_ascii(localebuf)) - { - elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", localebuf); - continue; - } - - enc = pg_get_encoding_from_locale(localebuf, false); + enc = create_collation_from_locale(localebuf, &nvalid, &ncreated, nspid); if (enc < 0) - { - elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", - localebuf); - continue; - } - if (!PG_VALID_BE_ENCODING(enc)) - { - elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", localebuf); continue; - } - if (enc == PG_SQL_ASCII) - continue; /* C/POSIX are already in the catalog */ - - /* count valid locales found in operating system */ - nvalid++; - - /* - * Create a collation named the same as the locale, but quietly - * doing nothing if it already exists. This is the behavior we - * need even at initdb time, because some versions of "locale -a" - * can report the same locale name more than once. And it's - * convenient for later import runs, too, since you just about - * always want to add on new locales without a lot of chatter - * about existing ones. - */ - collid = CollationCreate(localebuf, nspid, GetUserId(), - COLLPROVIDER_LIBC, true, enc, - localebuf, localebuf, NULL, - get_collation_actual_version(COLLPROVIDER_LIBC, localebuf), - true, true); - if (OidIsValid(collid)) - { - ncreated++; - - /* Must do CCI between inserts to handle duplicates correctly */ - CommandCounterIncrement(); - } /* * Generate aliases such as "en_US" in addition to "en_US.utf8" @@ -857,5 +969,29 @@ pg_import_system_collations(PG_FUNCTION_ARGS) } #endif /* USE_ICU */ + /* Load collations known to WIN32 */ +#ifdef ENUM_SYSTEM_LOCALE + { + int nvalid = 0; + CollParam param; + param.ncreated = &ncreated; + param.nvalid = &nvalid; + param.nspid = nspid; + + /* + * Enumerate the locales that are either installed on or supported + * by the OS. + */ + if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL, + (LPARAM) &param, NULL)) + _dosmaperr(GetLastError()); + + /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */ + if (nvalid == 0) + ereport(WARNING, + (errmsg("no usable system locales were found"))); + } +#endif /* ENUM_SYSTEM_LOCALE */ + PG_RETURN_INT32(ncreated); } -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2023-01-03 13:48                         ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  0 siblings, 1 reply; 32+ messages in thread

From: Peter Eisentraut @ 2023-01-03 13:48 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 09.12.22 13:48, Juan José Santamaría Flecha wrote:
> On Thu, Dec 1, 2022 at 8:46 AM Peter Eisentraut 
> <peter.eisentraut@enterprisedb.com 
> <mailto:peter.eisentraut@enterprisedb.com>> wrote:
> 
> 
>     What is the status of this now?  I think the other issue has been
>     addressed?
> 
> 
> Yes, that's addressed for MSVC builds. I think there are a couple of 
> pending issues for MinGW, but those should have their own threads.
> 
> The patch had rotten, so PFA a rebased version.

committed






^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-02-26 21:02                           ` Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  0 siblings, 1 reply; 32+ messages in thread

From: Andrew Dunstan @ 2023-02-26 21:02 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>


On 2023-01-03 Tu 08:48, Peter Eisentraut wrote:
> On 09.12.22 13:48, Juan José Santamaría Flecha wrote:
>> On Thu, Dec 1, 2022 at 8:46 AM Peter Eisentraut 
>> <peter.eisentraut@enterprisedb.com 
>> <mailto:peter.eisentraut@enterprisedb.com>> wrote:
>>
>>
>>     What is the status of this now?  I think the other issue has been
>>     addressed?
>>
>>
>> Yes, that's addressed for MSVC builds. I think there are a couple of 
>> pending issues for MinGW, but those should have their own threads.
>>
>> The patch had rotten, so PFA a rebased version.
>
> committed
>
>

Now that I have removed the barrier to testing this in the buildfarm, 
and added an appropriate locale setting to drongo, we can see that this 
test fails like this:


inline patch 1: c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out (+3/-2 lines) download inline-1.patchdiff -w -U3 c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out --- c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out 2023-01-23 04:39:06.755149600 +0000 +++ c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out 2023-02-26 17:32:54.115515200 +0000 @@ -363,16 +363,17 @@ -- to_char SET lc_time TO 'de_DE'; +ERROR: invalid value for parameter "lc_time": "de_DE" SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); to_char ------------- - 01 MRZ 2010 + 01 MAR 2010 (1 row) SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); to_char ------------- - 01 MRZ 2010 + 01 MAR 2010 (1 row) -- to_date
The last of these is especially an issue, as it doesn't even throw an error. See <https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo&dt=2023-02-26%2016%3A56%3A30; cheers andrew -- Andrew Dunstan EDB:https://www.enterprisedb.com ^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
@ 2023-02-27 12:09                             ` Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Andrew Dunstan @ 2023-02-27 12:09 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>


On 2023-02-26 Su 16:02, Andrew Dunstan wrote:
>
>
> On 2023-01-03 Tu 08:48, Peter Eisentraut wrote:
>> On 09.12.22 13:48, Juan José Santamaría Flecha wrote:
>>> On Thu, Dec 1, 2022 at 8:46 AM Peter Eisentraut 
>>> <peter.eisentraut@enterprisedb.com 
>>> <mailto:peter.eisentraut@enterprisedb.com>> wrote:
>>>
>>>
>>>     What is the status of this now?  I think the other issue has been
>>>     addressed?
>>>
>>>
>>> Yes, that's addressed for MSVC builds. I think there are a couple of 
>>> pending issues for MinGW, but those should have their own threads.
>>>
>>> The patch had rotten, so PFA a rebased version.
>>
>> committed
>>
>>
>
> Now that I have removed the barrier to testing this in the buildfarm, 
> and added an appropriate locale setting to drongo, we can see that 
> this test fails like this:
>
>
> diff -w -U3 c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out
> --- c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out	2023-01-23 04:39:06.755149600 +0000
> +++ c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out	2023-02-26 17:32:54.115515200 +0000
> @@ -363,16 +363,17 @@
>   
>   -- to_char
>   SET lc_time TO 'de_DE';
> +ERROR:  invalid value for parameter "lc_time": "de_DE"
>   SELECT to_char(date '2010-03-01', 'DD TMMON YYYY');
>      to_char
>   -------------
> - 01 MRZ 2010
> + 01 MAR 2010
>   (1 row)
>   
>   SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE");
>      to_char
>   -------------
> - 01 MRZ 2010
> + 01 MAR 2010
>   (1 row)
>   
>   -- to_date
>
>
> The last of these is especially an issue, as it doesn't even throw an 
> error.
>
> See 
> <https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo&dt=2023-02-26%2016%3A56%3A30;
>
>
>


Further investigation shows that if we change the two instances of 
"de_DE" to "de-DE" the tests behave as expected, so it appears that 
while POSIX style aliases have been created for the BCP 47 style 
locales, using the POSIX aliases doesn't in fact work. I cant see 
anything that turns the POSIX locale name back into BCP 47 at the point 
of use, which seems to be what's needed.


cheers


andrew

--
Andrew Dunstan
EDB:https://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
@ 2023-02-27 22:05                               ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-27 22:20                                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2023-02-27 22:05 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Feb 27, 2023 at 1:10 PM Andrew Dunstan <andrew@dunslane.net> wrote:

> On 2023-02-26 Su 16:02, Andrew Dunstan wrote:
>
> Now that I have removed the barrier to testing this in the buildfarm, and
> added an appropriate locale setting to drongo, we can see that this test
> fails like this:
>
>
> diff -w -U3 c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out
> --- c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/expected/collate.windows.win1252.out	2023-01-23 04:39:06.755149600 +0000
> +++ c:/prog/bf/root/HEAD/pgsql.build/src/test/regress/results/collate.windows.win1252.out	2023-02-26 17:32:54.115515200 +0000
> @@ -363,16 +363,17 @@
>
>  -- to_char
>  SET lc_time TO 'de_DE';
> +ERROR:  invalid value for parameter "lc_time": "de_DE"
>  SELECT to_char(date '2010-03-01', 'DD TMMON YYYY');
>     to_char
>  -------------
> - 01 MRZ 2010
> + 01 MAR 2010
>  (1 row)
>
>  SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE");
>     to_char
>  -------------
> - 01 MRZ 2010
> + 01 MAR 2010
>  (1 row)
>
>  -- to_date
>
>
> The last of these is especially an issue, as it doesn't even throw an
> error.
>
> See
> <https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo&dt=2023-02-26%2016%3A56%3A30;
> <https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo&dt=2023-02-26%2016%3A56%3A30;
>
>
> Further investigation shows that if we change the two instances of "de_DE"
> to "de-DE" the tests behave as expected, so it appears that while POSIX
> style aliases have been created for the BCP 47 style locales, using the
> POSIX aliases doesn't in fact work. I cant see anything that turns the
> POSIX locale name back into BCP 47 at the point of use, which seems to be
> what's needed.
>

The command that's failing is "SET lc_time TO 'de_DE';", and that area of
code is untouched by this patch. As mentioned in [1], the problem seems to
come from a Windows bug that the CI images and my development machines have
patched out.

I think we should change the locale name to make the test more robust, as
the attached. But I don't see a problem with making an alias for the
collations.

Regards,

Juan José Santamaría Flecha

Attachments:

0001-change-locale-name-for-test-collate.windows.win1252.patch (application/octet-stream, 1.5K) download From 11d75e5bf3cd0336f9cd08c637f9f2ab33d59a63 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Mon, 27 Feb 2023 17:00:19 -0500 Subject: [PATCH] change locale name for test collate.windows.win1252 The locale name 'de_DE' seems to be problematic on some Windows versions, change it to 'de-DE' to make the test more robust. --- src/test/regress/expected/collate.windows.win1252.out | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out index 4d538f2..869808c 100644 --- a/src/test/regress/expected/collate.windows.win1252.out +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -362,7 +362,7 @@ SELECT relname FROM pg_class WHERE relname ~* '^abc'; (0 rows) -- to_char -SET lc_time TO 'de_DE'; +SET lc_time TO 'de-DE'; SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); to_char ------------- diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql index 64f5ef9..132897e 100644 --- a/src/test/regress/sql/collate.windows.win1252.sql +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -152,7 +152,7 @@ SELECT relname FROM pg_class WHERE relname ~* '^abc'; -- to_char -SET lc_time TO 'de_DE'; +SET lc_time TO 'de-DE'; SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2023-02-27 22:20                                 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 11:55                                   ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2023-02-27 22:20 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

El lun, 27 feb 2023, 23:05, Juan José Santamaría Flecha <
juanjo.santamaria@gmail.com> escribió:

>
> The command that's failing is "SET lc_time TO 'de_DE';", and that area of
> code is untouched by this patch. As mentioned in [1], the problem seems to
> come from a Windows bug that the CI images and my development machines have
> patched out.
>

What I wanted to post as [1]:

https://www.postgresql.org/message-id/CAC%2BAXB1agvrgpyHEfqbDr2MOpcON3d%2BWYte_SLzn1E4TamLs9g%40mail...


> Regards,
>
> Juan José Santamaría Flecha
>

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-27 22:20                                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2023-02-28 11:55                                   ` Andrew Dunstan <andrew@dunslane.net>
  2023-02-28 16:40                                     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Andrew Dunstan @ 2023-02-28 11:55 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>


On 2023-02-27 Mo 17:20, Juan José Santamaría Flecha wrote:
>
>
> El lun, 27 feb 2023, 23:05, Juan José Santamaría Flecha 
> <juanjo.santamaria@gmail.com> escribió:
>
>
>     The command that's failing is "SET lc_time TO 'de_DE';", and that
>     area of code is untouched by this patch. As mentioned in [1],
>     the problem seems to come from a Windows bug that the CI images
>     and my development machines have patched out.
>
>
> What I wanted to post as [1]:
>
> https://www.postgresql.org/message-id/CAC%2BAXB1agvrgpyHEfqbDr2MOpcON3d%2BWYte_SLzn1E4TamLs9g%40mail...


Hmm, yeah. I'm not sure I understand the point of this test anyway:


SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE");


cheers


andrew

--
Andrew Dunstan
EDB:https://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-27 22:20                                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 11:55                                   ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
@ 2023-02-28 16:40                                     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 20:26                                       ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  0 siblings, 1 reply; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2023-02-28 16:40 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Feb 28, 2023 at 12:55 PM Andrew Dunstan <andrew@dunslane.net> wrote:

> On 2023-02-27 Mo 17:20, Juan José Santamaría Flecha wrote:
>
>
> Hmm, yeah. I'm not sure I understand the point of this test anyway:
>
>
> SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE");
>

Uhm, they probably don't make much sense except for "tr_TR", so I'm fine
with removing them. PFA a patch for so.

Regards,

Juan José Santamaría Flecha

Attachments: download all (2 files, .tar.gz)

0002-remove-useless-test-from-collate.windows.win1252.patch (application/octet-stream, 2.6K) download From 321d4ca0d75435880d2bb32d137b396c6dc210f6 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Tue, 28 Feb 2023 11:38:03 -0500 Subject: [PATCH] remove useless test from collate.windows.win1252 The to_char() and to_date() tests are useless without the 'tr_TR' collation. --- .../regress/expected/collate.windows.win1252.out | 24 ---------------------- src/test/regress/sql/collate.windows.win1252.sql | 13 ------------ 2 files changed, 37 deletions(-) diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out index 869808c..61b4211 100644 --- a/src/test/regress/expected/collate.windows.win1252.out +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -361,30 +361,6 @@ SELECT relname FROM pg_class WHERE relname ~* '^abc'; --------- (0 rows) --- to_char -SET lc_time TO 'de-DE'; -SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); - to_char -------------- - 01 MRZ 2010 -(1 row) - -SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); - to_char -------------- - 01 MRZ 2010 -(1 row) - --- to_date -SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); -ERROR: invalid value "MÄR" for "MON" -DETAIL: The given value did not match any of the allowed values for this field. -SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); -ERROR: invalid value "Mär" for "MON" -DETAIL: The given value did not match any of the allowed values for this field. -SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail -ERROR: invalid value "1234567890ab" for "MONTH" -DETAIL: The given value did not match any of the allowed values for this field. -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql index 132897e..b5c45e1 100644 --- a/src/test/regress/sql/collate.windows.win1252.sql +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -150,19 +150,6 @@ FROM collate_test6; SELECT relname FROM pg_class WHERE relname ~* '^abc'; --- to_char - -SET lc_time TO 'de-DE'; -SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); -SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); - --- to_date - -SELECT to_date('01 MÄR 2010', 'DD TMMON YYYY'); -SELECT to_date('01 Mär 2010', 'DD TMMON YYYY'); -SELECT to_date('1234567890ab 2010', 'TMMONTH YYYY'); -- fail - - -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; -- 2.11.0
0001-change-locale-name-for-test-collate.windows.win1252.patch (application/octet-stream, 1.5K) download From 11d75e5bf3cd0336f9cd08c637f9f2ab33d59a63 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com> Date: Mon, 27 Feb 2023 17:00:19 -0500 Subject: [PATCH] change locale name for test collate.windows.win1252 The locale name 'de_DE' seems to be problematic on some Windows versions, change it to 'de-DE' to make the test more robust. --- src/test/regress/expected/collate.windows.win1252.out | 2 +- src/test/regress/sql/collate.windows.win1252.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/collate.windows.win1252.out b/src/test/regress/expected/collate.windows.win1252.out index 4d538f2..869808c 100644 --- a/src/test/regress/expected/collate.windows.win1252.out +++ b/src/test/regress/expected/collate.windows.win1252.out @@ -362,7 +362,7 @@ SELECT relname FROM pg_class WHERE relname ~* '^abc'; (0 rows) -- to_char -SET lc_time TO 'de_DE'; +SET lc_time TO 'de-DE'; SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); to_char ------------- diff --git a/src/test/regress/sql/collate.windows.win1252.sql b/src/test/regress/sql/collate.windows.win1252.sql index 64f5ef9..132897e 100644 --- a/src/test/regress/sql/collate.windows.win1252.sql +++ b/src/test/regress/sql/collate.windows.win1252.sql @@ -152,7 +152,7 @@ SELECT relname FROM pg_class WHERE relname ~* '^abc'; -- to_char -SET lc_time TO 'de_DE'; +SET lc_time TO 'de-DE'; SELECT to_char(date '2010-03-01', 'DD TMMON YYYY'); SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE"); -- 2.11.0
^ permalink raw reply [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-27 22:20                                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 11:55                                   ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-28 16:40                                     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
@ 2023-02-28 20:26                                       ` Andrew Dunstan <andrew@dunslane.net>
  2023-03-01 08:49                                         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 1 reply; 32+ messages in thread

From: Andrew Dunstan @ 2023-02-28 20:26 UTC (permalink / raw)
  To: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>


On 2023-02-28 Tu 11:40, Juan José Santamaría Flecha wrote:
>
> On Tue, Feb 28, 2023 at 12:55 PM Andrew Dunstan <andrew@dunslane.net> 
> wrote:
>
>     On 2023-02-27 Mo 17:20, Juan José Santamaría Flecha wrote:
>
>
>     Hmm, yeah. I'm not sure I understand the point of this test anyway:
>
>
>     SELECT to_char(date '2010-03-01', 'DD TMMON YYYY' COLLATE "de_DE");
>
>
> Uhm, they probably don't make much sense except for "tr_TR", so I'm 
> fine with removing them. PFA a patch for so.
>
>

I think you missed my point, which was that the COLLATE clause above 
seemed particularly pointless. But I agree that all these are not much 
use, so I'll remove them as you suggest.


cheers


andrew

--
Andrew Dunstan
EDB:https://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 32+ messages in thread

* Re: WIN32 pg_import_system_collations
  2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
  2022-01-25 10:40 ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-01-25 14:49   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-03-22 01:00     ` Re: WIN32 pg_import_system_collations Andres Freund <andres@anarazel.de>
  2022-04-11 12:20       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-07-12 19:32         ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-10-31 14:09           ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-04 22:08             ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-07 15:08               ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-11-08 23:02                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-11-10 10:08                   ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2022-12-01 07:46                     ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2022-12-09 12:48                       ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-01-03 13:48                         ` Re: WIN32 pg_import_system_collations Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  2023-02-26 21:02                           ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 12:09                             ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-27 22:05                               ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-27 22:20                                 ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 11:55                                   ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
  2023-02-28 16:40                                     ` Re: WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  2023-02-28 20:26                                       ` Re: WIN32 pg_import_system_collations Andrew Dunstan <andrew@dunslane.net>
@ 2023-03-01 08:49                                         ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
  0 siblings, 0 replies; 32+ messages in thread

From: Juan José Santamaría Flecha @ 2023-03-01 08:49 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Dmitry Koval <d.koval@postgrespro.ru>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Feb 28, 2023 at 9:26 PM Andrew Dunstan <andrew@dunslane.net> wrote:

> I think you missed my point, which was that the COLLATE clause above
> seemed particularly pointless. But I agree that all these are not much use,
> so I'll remove them as you suggest.
>

Maybe there has been some miscommunication, please let me try to explain
myself a little better. The whole test is an attempt to mimic
collate.linux.utf8, which has that same command, only for collate 'tr_TR',
and so does collate.icu.utf8 but commented out.

I've seen that you have committed this and now drongo is green, which is
great. Thank you for taking care of it.

Regards,

Juan José Santamaría Flecha

^ permalink  raw  reply  [nested|flat] 32+ messages in thread


end of thread, other threads:[~2023-03-01 08:49 UTC | newest]

Thread overview: 32+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-12-13 08:41 WIN32 pg_import_system_collations Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2021-12-13 16:28 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2021-12-13 20:53   ` Thomas Munro <thomas.munro@gmail.com>
2021-12-14 20:13     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2021-12-14 21:45       ` Thomas Munro <thomas.munro@gmail.com>
2021-12-15 02:52         ` Michael Paquier <michael@paquier.xyz>
2021-12-15 04:03           ` Thomas Munro <thomas.munro@gmail.com>
2022-01-19 09:53   ` Julien Rouhaud <rjuju123@gmail.com>
2022-01-19 12:24     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-01-25 07:56       ` Dmitry Koval <d.koval@postgrespro.ru>
2022-01-24 21:23 Re: WIN32 pg_import_system_collations Dmitry Koval <d.koval@postgrespro.ru>
2022-01-25 10:40 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2022-01-25 14:49   ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-03-22 01:00     ` Andres Freund <andres@anarazel.de>
2022-04-11 12:20       ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-07-12 19:32         ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-10-31 14:09           ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2022-11-04 22:08             ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-11-07 15:08               ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2022-11-08 23:02                 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-11-10 10:08                   ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2022-12-01 07:46                     ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2022-12-09 12:48                       ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2023-01-03 13:48                         ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-26 21:02                           ` Andrew Dunstan <andrew@dunslane.net>
2023-02-27 12:09                             ` Andrew Dunstan <andrew@dunslane.net>
2023-02-27 22:05                               ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2023-02-27 22:20                                 ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2023-02-28 11:55                                   ` Andrew Dunstan <andrew@dunslane.net>
2023-02-28 16:40                                     ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>
2023-02-28 20:26                                       ` Andrew Dunstan <andrew@dunslane.net>
2023-03-01 08:49                                         ` Juan José Santamaría Flecha <juanjo.santamaria@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox