Skip to content

Commit e3269ca

Browse files
committed
Make PGLC_setlocale() static, and document that it can't be used safely
for any other purpose than PGLC_localeconv()'s internal save/restore of locale settings. Fix cash.c to call PGLC_localeconv() rather than making a direct call to localeconv() --- the old way, if PGLC_localeconv() had already cached a locale result, it would be overwritten by the first cash_in or cash_out operation, leading to wrong-locale results later. Probably no demonstrable bug today, since we only appear to be looking at the LC_MONETARY results which should be the same anyway, but definitely a gotcha waiting to strike.
1 parent 74dc04a commit e3269ca

File tree

3 files changed

+64
-53
lines changed

3 files changed

+64
-53
lines changed

src/backend/utils/adt/cash.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,23 @@
99
* workings can be found in the book "Software Solutions in C" by
1010
* Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
1111
*
12-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.47 2000/11/25 20:33:52 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.48 2000/11/25 22:43:08 tgl Exp $
1313
*/
1414

15+
#include "postgres.h"
16+
1517
#include <limits.h>
1618
#include <ctype.h>
1719
#include <math.h>
20+
#ifdef USE_LOCALE
1821
#include <locale.h>
22+
#endif
1923

20-
#include "postgres.h"
2124
#include "miscadmin.h"
2225
#include "utils/builtins.h"
2326
#include "utils/cash.h"
27+
#include "utils/pg_locale.h"
28+
2429

2530
static const char *num_word(Cash value);
2631

@@ -31,11 +36,6 @@ static const char *num_word(Cash value);
3136
#define LAST_PAREN (TERMINATOR - 1)
3237
#define LAST_DIGIT (LAST_PAREN - 1)
3338

34-
#ifdef USE_LOCALE
35-
static struct lconv *lconvert = NULL;
36-
37-
#endif
38-
3939

4040
/*
4141
* Cash is a pass-by-ref SQL type, so we must pass and return pointers.
@@ -82,11 +82,11 @@ cash_in(PG_FUNCTION_ARGS)
8282
ssymbol,
8383
psymbol,
8484
*nsymbol;
85-
8685
#ifdef USE_LOCALE
87-
if (lconvert == NULL)
88-
lconvert = localeconv();
86+
struct lconv *lconvert = PGLC_localeconv();
87+
#endif
8988

89+
#ifdef USE_LOCALE
9090
/*
9191
* frac_digits will be CHAR_MAX in some locales, notably C. However,
9292
* just testing for == CHAR_MAX is risky, because of compilers like
@@ -238,11 +238,11 @@ cash_out(PG_FUNCTION_ARGS)
238238
dsymbol,
239239
*nsymbol;
240240
char convention;
241-
242241
#ifdef USE_LOCALE
243-
if (lconvert == NULL)
244-
lconvert = localeconv();
242+
struct lconv *lconvert = PGLC_localeconv();
243+
#endif
245244

245+
#ifdef USE_LOCALE
246246
/* see comments about frac_digits in cash_in() */
247247
points = lconvert->frac_digits;
248248
if (points < 0 || points > 10)

src/backend/utils/adt/pg_locale.c

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
21
/* -----------------------------------------------------------------------
32
* pg_locale.c
43
*
5-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.6 2000/08/29 04:41:47 momjian Exp $
4+
* The PostgreSQL locale utils.
65
*
76
*
8-
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
7+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.7 2000/11/25 22:43:08 tgl Exp $
98
*
10-
* The PostgreSQL locale utils.
9+
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
1110
*
1211
* Karel Zak - Zakkr
1312
*
1413
* -----------------------------------------------------------------------
1514
*/
1615

17-
#include <stdio.h>
18-
1916
#include "postgres.h"
2017

2118
#ifdef USE_LOCALE
@@ -28,11 +25,13 @@
2825

2926
static struct lconv *CurrentLocaleConv = NULL;
3027

28+
static void PGLC_setlocale(PG_LocaleCategories * lc);
29+
3130
/*------
32-
* Return in PG_LocaleCategories current locale setting
31+
* Return in PG_LocaleCategories the current locale settings
3332
*------
3433
*/
35-
PG_LocaleCategories *
34+
void
3635
PGLC_current(PG_LocaleCategories * lc)
3736
{
3837
lc->lang = getenv("LANG");
@@ -45,7 +44,6 @@ PGLC_current(PG_LocaleCategories * lc)
4544
#ifdef LC_MESSAGES
4645
lc->lc_messages = setlocale(LC_MESSAGES, NULL);
4746
#endif
48-
return lc;
4947
}
5048

5149

@@ -55,7 +53,7 @@ PGLC_current(PG_LocaleCategories * lc)
5553
* Print a PG_LocaleCategories struct as DEBUG
5654
*------
5755
*/
58-
PG_LocaleCategories *
56+
static void
5957
PGLC_debug_lc(PG_LocaleCategories * lc)
6058
{
6159
#ifdef LC_MESSAGES
@@ -73,72 +71,86 @@ PGLC_debug_lc(PG_LocaleCategories * lc)
7371
, lc->lc_messages
7472
#endif
7573
);
76-
77-
return lc;
7874
}
7975

8076
#endif
8177

8278
/*------
8379
* Set locales via a PG_LocaleCategories struct
80+
*
81+
* NB: it would be very dangerous to set the locale values to any random
82+
* choice of locale, since that could cause indexes to become corrupt, etc.
83+
* Therefore this routine is NOT exported from this module. It should be
84+
* used only to restore previous locale settings during PGLC_localeconv.
8485
*------
8586
*/
86-
PG_LocaleCategories *
87+
static void
8788
PGLC_setlocale(PG_LocaleCategories * lc)
8889
{
90+
if (!setlocale(LC_COLLATE, lc->lc_collate))
91+
elog(NOTICE, "pg_setlocale(): 'LC_COLLATE=%s' cannot be honored.",
92+
lc->lc_collate);
93+
8994
if (!setlocale(LC_CTYPE, lc->lc_ctype))
90-
elog(NOTICE, "pg_setlocale(): 'LC_CTYPE=%s' cannot be honored.", lc->lc_ctype);
95+
elog(NOTICE, "pg_setlocale(): 'LC_CTYPE=%s' cannot be honored.",
96+
lc->lc_ctype);
9197

9298
if (!setlocale(LC_NUMERIC, lc->lc_numeric))
93-
elog(NOTICE, "pg_setlocale(): 'LC_NUMERIC=%s' cannot be honored.", lc->lc_numeric);
99+
elog(NOTICE, "pg_setlocale(): 'LC_NUMERIC=%s' cannot be honored.",
100+
lc->lc_numeric);
94101

95102
if (!setlocale(LC_TIME, lc->lc_time))
96-
elog(NOTICE, "pg_setlocale(): 'LC_TIME=%s' cannot be honored.", lc->lc_time);
97-
98-
if (!setlocale(LC_COLLATE, lc->lc_collate))
99-
elog(NOTICE, "pg_setlocale(): 'LC_COLLATE=%s' cannot be honored.", lc->lc_collate);
103+
elog(NOTICE, "pg_setlocale(): 'LC_TIME=%s' cannot be honored.",
104+
lc->lc_time);
100105

101106
if (!setlocale(LC_MONETARY, lc->lc_monetary))
102-
elog(NOTICE, "pg_setlocale(): 'LC_MONETARY=%s' cannot be honored.", lc->lc_monetary);
107+
elog(NOTICE, "pg_setlocale(): 'LC_MONETARY=%s' cannot be honored.",
108+
lc->lc_monetary);
109+
103110
#ifdef LC_MESSAGES
104111
if (!setlocale(LC_MESSAGES, lc->lc_messages))
105-
elog(NOTICE, "pg_setlocale(): 'LC_MESSAGE=%s' cannot be honored.", lc->lc_messages);
112+
elog(NOTICE, "pg_setlocale(): 'LC_MESSAGE=%s' cannot be honored.",
113+
lc->lc_messages);
106114
#endif
107-
return lc;
108115
}
109116

110117
/*------
111118
* Return the POSIX lconv struct (contains number/money formatting information)
112-
* with locale information for *all* categories.
113-
* => Returned lconv is *independent* on current locale catogories setting - in
114-
* contrast to standard localeconv().
119+
* with locale information for all categories. Note that returned lconv
120+
* does not depend on currently active category settings, but on external
121+
* environment variables for locale.
122+
*
123+
* XXX we assume that restoring old category settings via setlocale() will
124+
* not immediately corrupt the static data returned by localeconv().
125+
* How portable is this?
115126
*
116-
* ! libc prepare memory space for lconv itself and all returned strings in
117-
* lconv are *static strings*.
127+
* XXX in any case, there certainly must not be any other calls to
128+
* localeconv() anywhere in the backend, else the values reported here
129+
* will be overwritten with the Postgres-internal locale settings.
118130
*------
119131
*/
120132
struct lconv *
121133
PGLC_localeconv(void)
122134
{
123135
PG_LocaleCategories lc;
124-
136+
137+
/* Did we do it already? */
125138
if (CurrentLocaleConv)
126139
return CurrentLocaleConv;
127140

128141
/* Save current locale setting to lc */
129142
PGLC_current(&lc);
130143

131-
/* Set all locale category for current lang */
144+
/* Set all locale categories based on postmaster's environment vars */
132145
setlocale(LC_ALL, "");
133146

134-
/* Get numeric formatting information */
147+
/* Get formatting information for the external environment */
135148
CurrentLocaleConv = localeconv();
136149

137-
/* Set previous original locale */
150+
/* Restore Postgres' internal locale settings */
138151
PGLC_setlocale(&lc);
139152

140153
return CurrentLocaleConv;
141154
}
142155

143-
144156
#endif /* USE_LOCALE */

src/include/utils/pg_locale.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
21
/* -----------------------------------------------------------------------
32
* pg_locale.h
43
*
5-
* $Header: /cvsroot/pgsql/src/include/utils/pg_locale.h,v 1.4 2000/04/12 17:16:55 momjian Exp $
4+
* The PostgreSQL locale utils.
65
*
76
*
8-
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
7+
* $Id: pg_locale.h,v 1.5 2000/11/25 22:43:07 tgl Exp $
98
*
10-
* The PostgreSQL locale utils.
9+
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
1110
*
1211
* Karel Zak - Zakkr
1312
*
@@ -35,13 +34,13 @@ typedef struct PG_LocaleCategories
3534
} PG_LocaleCategories;
3635

3736

38-
extern PG_LocaleCategories *PGLC_current(PG_LocaleCategories * lc);
39-
extern PG_LocaleCategories *PGLC_setlocale(PG_LocaleCategories * lc);
37+
extern void PGLC_current(PG_LocaleCategories * lc);
4038

4139
/*------
4240
* Return the POSIX lconv struct (contains number/money formatting information)
43-
* with locale information for *all* categories. Returned lconv is *independent*
44-
* on current locale catogories setting - in contrast to standard localeconv().
41+
* with locale information for all categories. Note that returned lconv
42+
* does not depend on currently active category settings, but on external
43+
* environment variables for locale.
4544
*------
4645
*/
4746
extern struct lconv *PGLC_localeconv(void);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy