Windows locales and tests portability

Started by Andrey Borodin9 months ago5 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t53261
psql -h localhost -U postgres

Built from patchset v1 (message #1), October 23, 2026 at 11:30 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t53261_1 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t53261_1 && git checkout t53261_1

Patchset v1 (message #1) is on t53261_1

Jump to latest
#1Andrey Borodin
amborodin@acm.org

Hi hackers!

I'm toying with Windows machine. It's nice, fast, run cool games, so I decided to keep it and make a BF animal on top. The problem is I have RU locale.

PostgreSQL::Test::Utils.pm (lines 113-115) explicitly sets:
$ENV{LC_NUMERIC} = 'C';
setlocale(LC_ALL, "");
This makes Perl format numbers with . as the decimal separator.

Windows MSVC strtod(): seems to ignore environment variables entirely and use the system's regional settings (Russian locale ,)
On Unix, environment variables like LC_NUMERIC control both Perl and C library functions like strtod(). Looks like on Windows with MSVC, the C runtime gets its locale from the Windows regional settings, not from environment variables.

FWIW I used strawberry perl 5, version 42, subversion 0 (v5.42.0) built for MSWin32-x64-multi-thread. Windows 11 Pro 23H2 build 22631.4890.

I propose attached ugly fix, it makes my box happy and works on CI machines.

In pg_test_timing fix is easy. It just accept [,.] as a decimal separator. AFAIK that would solve the problem for Russian, Brazilian, Indonesia, German, Italian, Polish etc locales. There are some other separators, but with really small fraction, I could add them into regex, but with a wiki-page-like comment... so here's [,.]

For \watch tests we need to format number that would be acceptable to strtod(), so I resorted to printing this number by psql. Luckily in standard SQL syntax so far, the period (.) is the universal decimal separator for numeric literals in a query.

Perhaps, I should just switch my machine to locale with dot too. But just in case if we really want tests on Windows with locales, PFA.
I did not consider seriously bashing with Utils.pm setlocale(). But maybe I should try.

WDYT?

Best regards, Andrey Borodin.

Attachments:

t53261_1
0001-Fix-TAP-tests-for-Windows-with-non-C-numeric-locales.patchapplication/octet-stream; name=0001-Fix-TAP-tests-for-Windows-with-non-C-numeric-locales.patch; x-unix-mode=0644Download+59-12
#2Thomas Munro
thomas.munro@gmail.com
In reply to: Andrey Borodin (#1)
Re: Windows locales and tests portability

On Fri, Feb 6, 2026 at 8:03 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Windows MSVC strtod(): seems to ignore environment variables entirely and use the system's regional settings (Russian locale ,)

Hmm, right, we probably do setlocale(..., ""), which gets you an
"implementation-defined native environment", and Windows implements
that as you say. I suppose if we wanted it to work like Unix, we
could call getenv("LC_XXX") and feed the results to setlocale()
ourselves... maybe a terrible idea, IDK.

#3Andrew Bille
andrewbille@gmail.com
In reply to: Andrey Borodin (#1)
Re: Windows locales and tests portability

Hi Andrey,

I recently ran into the same problem independently while running the TAP
tests on Windows with the system locale set to Russian.

At first I saw the same pg_test_timing failures with a comma as the
decimal separator, but while investigating it I also found that this is
not only a test-output portability issue. For example, psql fails to
parse:

\watch 0.01

under a Windows user locale using a comma decimal separator, even though
the TAP environment sets LC_NUMERIC=C.

So I ended up trying the approach Thomas mentioned in this thread:
explicitly honor LC_NUMERIC from the environment on Windows after
setlocale(LC_ALL, "").

I posted a patch here:

/messages/by-id/CAJnzarxw2pJjVGQpwosjzBW+MO67vqSJt4jjVD5T3GpKpX+dkQ@mail.gmail.com

The patch only applies a non-empty LC_NUMERIC; if it is unset or empty,
the existing Windows behavior is unchanged. With it, both
pg_test_timing formatting and psql decimal \watch parsing behave as
expected under a Russian Windows locale.

Looks like I mostly rediscovered the issue from this thread, but the
\watch case convinced me that fixing it in frontend locale
initialization is preferable to teaching individual tests about Windows
decimal separators.

Regards,
Andrew

Show quoted text

On Tue, Sep 22, 2026 at 2:15 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Hi hackers!

I'm toying with Windows machine. It's nice, fast, run cool games, so I decided to keep it and make a BF animal on top. The problem is I have RU locale.

PostgreSQL::Test::Utils.pm (lines 113-115) explicitly sets:
$ENV{LC_NUMERIC} = 'C';
setlocale(LC_ALL, "");
This makes Perl format numbers with . as the decimal separator.

Windows MSVC strtod(): seems to ignore environment variables entirely and use the system's regional settings (Russian locale ,)
On Unix, environment variables like LC_NUMERIC control both Perl and C library functions like strtod(). Looks like on Windows with MSVC, the C runtime gets its locale from the Windows regional settings, not from environment variables.

FWIW I used strawberry perl 5, version 42, subversion 0 (v5.42.0) built for MSWin32-x64-multi-thread. Windows 11 Pro 23H2 build 22631.4890.

I propose attached ugly fix, it makes my box happy and works on CI machines.

In pg_test_timing fix is easy. It just accept [,.] as a decimal separator. AFAIK that would solve the problem for Russian, Brazilian, Indonesia, German, Italian, Polish etc locales. There are some other separators, but with really small fraction, I could add them into regex, but with a wiki-page-like comment... so here's [,.]

For \watch tests we need to format number that would be acceptable to strtod(), so I resorted to printing this number by psql. Luckily in standard SQL syntax so far, the period (.) is the universal decimal separator for numeric literals in a query.

Perhaps, I should just switch my machine to locale with dot too. But just in case if we really want tests on Windows with locales, PFA.
I did not consider seriously bashing with Utils.pm setlocale(). But maybe I should try.

WDYT?

Best regards, Andrey Borodin.

#4Andrey Borodin
amborodin@acm.org
In reply to: Andrew Bille (#3)
Re: Windows locales and tests portability

Hi Andrew,

On Sep 22, 2026, Andrew Bille wrote:

fixing it in frontend locale
initialization is preferable to teaching individual tests about
Windows decimal separators.

My patch was intended to adapt the tests to the existing Windows
behavior. Yours changes that behavior for frontend users, which seems
worth discussing separately. A non-empty LC_NUMERIC may already be
inherited by scripts that currently work because Windows ignores it.

There is also the precedence question: this patch applies LC_NUMERIC
without checking LC_ALL, so it does not quite reproduce POSIX behavior.
I'd propose to fix the tests independently while discussing what locale
environment handling we want on Windows.

Thank you!

Best regards, Andrew Borodin.

#5Andrew Bille
andrewbille@gmail.com
In reply to: Andrey Borodin (#4)
Re: Windows locales and tests portability

Hi Andrey,

Thanks, that makes sense.

I agree that fixing the tests independently is reasonable, and that my
patch is a separate behavior change rather than just a test fix.

You're also right about LC_ALL precedence. The patch as posted does
not reproduce POSIX semantics in that respect. If we want to honor
locale environment variables on Windows, it should probably follow the
usual precedence rules rather than applying LC_NUMERIC unconditionally.

The reason I started looking beyond the tests is that I found a
user-visible case as well: with a Windows locale using a comma decimal
separator, psql rejects for example:

\watch 0.01

even when the environment contains LC_NUMERIC=C.

So I still think there is a frontend behavior question worth discussing,
but I agree it should not block an independent test-side fix.

I'll update the discussion in my thread accordingly:

/messages/by-id/CAJnzarxw2pJjVGQpwosjzBW+MO67vqSJt4jjVD5T3GpKpX+dkQ@mail.gmail.com

Thanks!

Best regards,
Andrew

Show quoted text

On Tue, Sep 22, 2026 at 2:40 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Hi Andrew,

On Sep 22, 2026, Andrew Bille wrote:

fixing it in frontend locale
initialization is preferable to teaching individual tests about
Windows decimal separators.

My patch was intended to adapt the tests to the existing Windows
behavior. Yours changes that behavior for frontend users, which seems
worth discussing separately. A non-empty LC_NUMERIC may already be
inherited by scripts that currently work because Windows ignores it.

There is also the precedence question: this patch applies LC_NUMERIC
without checking LC_ALL, so it does not quite reproduce POSIX behavior.
I'd propose to fix the tests independently while discussing what locale
environment handling we want on Windows.

Thank you!

Best regards, Andrew Borodin.