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)