mirror of https://github.com/postgres/postgres
Remove setlocale() and _configthreadlocal() as fallback strategy on systems that don't have uselocale(), where ECPG tries to control LC_NUMERIC formatting on input and output of floating point numbers. It was probably broken on some systems (NetBSD), and the code was also quite messy and complicated, with obsolete configure tests (Windows). It was also arguably broken, or at least had unstated environmental requirements, if pgtypeslib code was called directly. Instead, introduce PG_C_LOCALE to refer to the "C" locale as a locale_t value. It maps to the special constant LC_C_LOCALE when defined by libc (macOS, NetBSD), or otherwise uses a process-lifetime locale_t that is allocated on first use, just as ECPG previously did itself. The new replacement might be more widely useful. Then change the float parsing and printing code to pass that to _l() functions where appropriate. Unfortunately the portability of those functions is a bit complicated. First, many obvious and useful _l() functions are missing from POSIX, though most standard libraries define some of them anyway. Second, although the thread-safe save/restore technique can be used to replace the missing ones, Windows and NetBSD refused to implement standard uselocale(). They might have a point: "wide scope" uselocale() is hard to combine with other code and error-prone, especially in library code. Luckily they have the _l() functions we want so far anyway. So we have to be prepared for both ways of doing things: 1. In ECPG, use strtod_l() for parsing, and supply a port.h replacement using uselocale() over a limited scope if missing. 2. Inside our own snprintf.c, use three different approaches to format floats. For frontend code, call libc's snprintf_l(), or wrap libc's snprintf() in uselocale() if it's missing. For backend code, snprintf.c can keep assuming that the global locale's LC_NUMERIC is "C" and call libc's snprintf() without change, for now. (It might eventually be possible to call our in-tree Ryū routines to display floats in snprintf.c, given the C-locale-always remit of our in-tree snprintf(), but this patch doesn't risk changing anything that complicated.) Author: Thomas Munro <thomas.munro@gmail.com> Reviewed-by: Peter Eisentraut <peter@eisentraut.org> Reviewed-by: Tristan Partin <tristan@partin.io> Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi> Discussion: https://postgr.es/m/CWZBBRR6YA8D.8EHMDRGLCKCD%40neon.techpull/208/head
parent
2247281c47
commit
8e993bff53
@ -0,0 +1,84 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* locale.c |
||||
* Helper routines for thread-safe system locale usage. |
||||
* |
||||
* |
||||
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/port/locale.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "c.h" |
||||
|
||||
#ifndef LC_C_LOCALE |
||||
|
||||
#ifndef WIN32 |
||||
#include <pthread.h> |
||||
#else |
||||
#include <synchapi.h> |
||||
#endif |
||||
|
||||
/* A process-lifetime singleton, allocated on first need. */ |
||||
static locale_t c_locale; |
||||
|
||||
#ifndef WIN32 |
||||
static void |
||||
init_c_locale_once(void) |
||||
{ |
||||
c_locale = newlocale(LC_ALL, "C", NULL); |
||||
} |
||||
#else |
||||
static BOOL |
||||
init_c_locale_once(PINIT_ONCE once, PVOID parameter, PVOID *context) |
||||
{ |
||||
c_locale = _create_locale(LC_ALL, "C"); |
||||
return true; |
||||
} |
||||
#endif |
||||
|
||||
/*
|
||||
* Access a process-lifetime singleton locale_t object. Use the macro |
||||
* PG_C_LOCALE instead of calling this directly, as it can skip the function |
||||
* call on some systems. |
||||
*/ |
||||
locale_t |
||||
pg_get_c_locale(void) |
||||
{ |
||||
/*
|
||||
* Fast path if already initialized. This assumes that we can read a |
||||
* locale_t (in practice, a pointer) without tearing in a multi-threaded |
||||
* program. |
||||
*/ |
||||
if (c_locale != (locale_t) 0) |
||||
return c_locale; |
||||
|
||||
/* Make a locale_t. It will live until process exit. */ |
||||
{ |
||||
#ifndef WIN32 |
||||
static pthread_once_t once = PTHREAD_ONCE_INIT; |
||||
|
||||
pthread_once(&once, init_c_locale_once); |
||||
#else |
||||
static INIT_ONCE once; |
||||
InitOnceExecuteOnce(&once, init_c_locale_once, NULL, NULL); |
||||
#endif |
||||
} |
||||
|
||||
/*
|
||||
* It's possible that the allocation of the locale failed due to low |
||||
* memory, and then (locale_t) 0 will be returned. Users of PG_C_LOCALE |
||||
* should defend against that by checking pg_ensure_c_locale() at a |
||||
* convenient time, so that they can treat it as a simple constant after |
||||
* that. |
||||
*/ |
||||
|
||||
return c_locale; |
||||
} |
||||
|
||||
#endif /* not LC_C_LOCALE */ |
||||
Loading…
Reference in new issue