[PATCH] BUG: Stale LC_MESSAGES translations after SET on windows.

From: Bryan Green <dbryan(dot)green(at)gmail(dot)com>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: [PATCH] BUG: Stale LC_MESSAGES translations after SET on windows.
Date: 2026-04-01 05:24:06
Message-ID: 70926239-1062-41c6-a831-f29b215d57bb@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Greetings hackers,

The libintl library provides a libintl_setlocale function that we need
to use to correctly invalidate the libintl translation cache. We are
not doing this as the following shows.

On Windows, changing lc_messages at runtime via SET returns stale
translations from the previous locale. For example:

SET lc_messages = 'de_DE';
SELECT 1/0;
-- FEHLER: Division durch Null (correct, German)

SET lc_messages = 'en_US';
SELECT 1/0;
-- FEHLER: Division durch Null (wrong, still German)

libintl caches translated messages in a binary tree keyed by (msgid,
domain, category). The key struct includes a counter and a pointer to
the translation text and translation length in the mmap'd .mo file for
that locale.

Background:
Assume you have ("PL/pgSQL function %s line %d at %s",
"plpgsql-19",1729) for (msgid, domain, category) respectively. Gettext
uses strcmp for msgid and domain, and integer subtraction for category
as it's comparison function. The search through the tree will short
circuit at a node as soon as one of the three-- compared left to right
as listed in the tuple-- is not a match. If the comparison is negative
then it goes left in the tree, positive it goes right. If all three
components of the key match then it checks that the counter matches
_nl_msg_cat_cntr. If the counter matches then it is a cache hit. If
the counter does not match then this translation was stored in the cache
before the current locale was set with libintl_setlocale-- in other
words it is stale. In this case the cache is invalidated and it is a
cache miss because the locale has changed.

Notice locale is not directly involved in the key structure for windows.
So, if you don't increment the counter by calling libintl_setlocale and
the two locales have the same msgid's for a given domain you will get
the problem shown at the beginning of this email.

The fix adds an explicit call to libintl_setlocale(LC_MESSAGES, ...)
in pg_perm_setlocale() after IsoLocaleName() has produced the POSIX
name. Instead of an explicit call, we could define
_INTL_REDIRECT_MACROS before including libintl.h in c.h. This would
make all setlocale() calls go through libintl_setlocale on MSVC. I
chose to be explicit to limit the blast radius. The attached patch
limits the change to a single spot and resolves the identified bug.

Patch attached.

--
Bryan Green
EDB: https://www.enterprisedb.com

Attachment Content-Type Size
v1-0001-Fix-stale-LC_MESSAGES-translations.patch text/plain 1.6 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Nisha Moond 2026-04-01 05:33:27 Re: Use SIGTERM instead of SIGUSR1 for slotsync worker to exit during promotion?
Previous Message Junwang Zhao 2026-04-01 05:13:30 Re: More speedups for tuple deformation