Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5093944

Browse files
committed
Fix assorted bogosities in cash_in() and cash_out().
cash_out failed to handle multiple-byte thousands separators, as per bug#6277 from Alexander Law. In addition, cash_in didn't handle that either,nor could it handle multiple-byte positive_sign. Both routines failed tosupport multiple-byte mon_decimal_point, which I did not think was worthchanging, but at least now they check for the possibility and fall back tousing '.' rather than emitting invalid output. Also, make cash_in handletrailing negative signs, which formerly it would reject. Since cash_outgenerates trailing negative signs whenever the locale tells it to, thislast omission represents a fail-to-reload-dumped-data bug. IMO thatjustifies patching this all the way back.
1 parent0418bea commit5093944

File tree

1 file changed

+109
-95
lines changed

1 file changed

+109
-95
lines changed

‎src/backend/utils/adt/cash.c

Lines changed: 109 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@
2828
#include"utils/cash.h"
2929
#include"utils/pg_locale.h"
3030

31-
#defineCASH_BUFSZ36
32-
33-
#defineTERMINATOR(CASH_BUFSZ - 1)
34-
#defineLAST_PAREN(TERMINATOR - 1)
35-
#defineLAST_DIGIT(LAST_PAREN - 1)
36-
3731

3832
/*************************************************************************
3933
* Private routines
@@ -106,13 +100,13 @@ cash_in(PG_FUNCTION_ARGS)
106100
Cashvalue=0;
107101
Cashdec=0;
108102
Cashsgn=1;
109-
intseen_dot=0;
103+
boolseen_dot=false;
110104
constchar*s=str;
111105
intfpoint;
112-
chardsymbol,
113-
ssymbol,
114-
psymbol;
115-
constchar*nsymbol,
106+
chardsymbol;
107+
constchar*ssymbol,
108+
*psymbol,
109+
*nsymbol,
116110
*csymbol;
117111

118112
structlconv*lconvert=PGLC_localeconv();
@@ -131,18 +125,22 @@ cash_in(PG_FUNCTION_ARGS)
131125
if (fpoint<0||fpoint>10)
132126
fpoint=2;/* best guess in this case, I think */
133127

134-
dsymbol= ((*lconvert->mon_decimal_point!='\0') ?*lconvert->mon_decimal_point :'.');
135-
if (*lconvert->mon_thousands_sep!='\0')
136-
ssymbol=*lconvert->mon_thousands_sep;
128+
/* we restrict dsymbol to be a single byte, but not the other symbols */
129+
if (*lconvert->mon_decimal_point!='\0'&&
130+
lconvert->mon_decimal_point[1]=='\0')
131+
dsymbol=*lconvert->mon_decimal_point;
137132
else
138-
/* ssymbol should not equal dsymbol */
139-
ssymbol= (dsymbol!=',') ?',' :'.';
140-
csymbol= ((*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$");
141-
psymbol= ((*lconvert->positive_sign!='\0') ?*lconvert->positive_sign :'+');
142-
nsymbol= ((*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-");
133+
dsymbol='.';
134+
if (*lconvert->mon_thousands_sep!='\0')
135+
ssymbol=lconvert->mon_thousands_sep;
136+
else/* ssymbol should not equal dsymbol */
137+
ssymbol= (dsymbol!=',') ?"," :".";
138+
csymbol= (*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$";
139+
psymbol= (*lconvert->positive_sign!='\0') ?lconvert->positive_sign :"+";
140+
nsymbol= (*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-";
143141

144142
#ifdefCASHDEBUG
145-
printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n",
143+
printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
146144
fpoint,dsymbol,ssymbol,csymbol,psymbol,nsymbol);
147145
#endif
148146

@@ -164,22 +162,20 @@ cash_in(PG_FUNCTION_ARGS)
164162
{
165163
sgn=-1;
166164
s+=strlen(nsymbol);
167-
#ifdefCASHDEBUG
168-
printf("cashin- negative symbol; string is '%s'\n",s);
169-
#endif
170165
}
171166
elseif (*s=='(')
172167
{
173168
sgn=-1;
174169
s++;
175170
}
176-
elseif (*s==psymbol)
177-
s++;
171+
elseif (strncmp(s,psymbol,strlen(psymbol))==0)
172+
s+=strlen(psymbol);
178173

179174
#ifdefCASHDEBUG
180175
printf("cashin- string is '%s'\n",s);
181176
#endif
182177

178+
/* allow whitespace and currency symbol after the sign, too */
183179
while (isspace((unsignedchar)*s))
184180
s++;
185181
if (strncmp(s,csymbol,strlen(csymbol))==0)
@@ -189,7 +185,7 @@ cash_in(PG_FUNCTION_ARGS)
189185
printf("cashin- string is '%s'\n",s);
190186
#endif
191187

192-
for (;;s++)
188+
for (;*s;s++)
193189
{
194190
/* we look for digits as long as we have found less */
195191
/* than the required number of decimal places */
@@ -203,33 +199,44 @@ cash_in(PG_FUNCTION_ARGS)
203199
/* decimal point? then start counting fractions... */
204200
elseif (*s==dsymbol&& !seen_dot)
205201
{
206-
seen_dot=1;
202+
seen_dot=true;
207203
}
208204
/* ignore if "thousands" separator, else we're done */
209-
elseif (*s!=ssymbol)
210-
{
211-
/* round off */
212-
if (isdigit((unsignedchar)*s)&&*s >='5')
213-
value++;
214-
215-
/* adjust for less than required decimal places */
216-
for (;dec<fpoint;dec++)
217-
value *=10;
218-
205+
elseif (strncmp(s,ssymbol,strlen(ssymbol))==0)
206+
s+=strlen(ssymbol)-1;
207+
else
219208
break;
220-
}
221209
}
222210

223-
/* should only be trailing digits followed by whitespace or right paren */
211+
/* round off if there's another digit */
212+
if (isdigit((unsignedchar)*s)&&*s >='5')
213+
value++;
214+
215+
/* adjust for less than required decimal places */
216+
for (;dec<fpoint;dec++)
217+
value *=10;
218+
219+
/*
220+
* should only be trailing digits followed by whitespace, right paren,
221+
* or possibly a trailing minus sign
222+
*/
224223
while (isdigit((unsignedchar)*s))
225224
s++;
226-
while (isspace((unsignedchar)*s)||*s==')')
227-
s++;
228-
229-
if (*s!='\0')
230-
ereport(ERROR,
231-
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
232-
errmsg("invalid input syntax for type money: \"%s\"",str)));
225+
while (*s)
226+
{
227+
if (isspace((unsignedchar)*s)||*s==')')
228+
s++;
229+
elseif (strncmp(s,nsymbol,strlen(nsymbol))==0)
230+
{
231+
sgn=-1;
232+
s+=strlen(nsymbol);
233+
}
234+
else
235+
ereport(ERROR,
236+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
237+
errmsg("invalid input syntax for type money: \"%s\"",
238+
str)));
239+
}
233240

234241
result=value*sgn;
235242

@@ -242,26 +249,24 @@ cash_in(PG_FUNCTION_ARGS)
242249

243250

244251
/* cash_out()
245-
* Function to convert cash to a dollars and cents representation.
246-
* XXX HACK This code appears to assume US conventions for
247-
*positive-valued amounts. - tgl 97/04/14
252+
* Function to convert cash to a dollars and cents representation, using
253+
* the lc_monetary locale's formatting.
248254
*/
249255
Datum
250256
cash_out(PG_FUNCTION_ARGS)
251257
{
252258
Cashvalue=PG_GETARG_CASH(0);
253259
char*result;
254-
charbuf[CASH_BUFSZ];
255-
intminus=0;
256-
intcount=LAST_DIGIT;
257-
intpoint_pos;
258-
intssymbol_position=0;
260+
charbuf[128];
261+
char*bufptr;
262+
boolminus= false;
263+
intdigit_pos;
259264
intpoints,
260265
mon_group;
261-
charssymbol;
262-
constchar*csymbol,
263-
*nsymbol;
264266
chardsymbol;
267+
constchar*ssymbol,
268+
*csymbol,
269+
*nsymbol;
265270
charconvention;
266271

267272
structlconv*lconvert=PGLC_localeconv();
@@ -280,69 +285,78 @@ cash_out(PG_FUNCTION_ARGS)
280285
mon_group=3;
281286

282287
convention=lconvert->n_sign_posn;
283-
dsymbol= ((*lconvert->mon_decimal_point!='\0') ?*lconvert->mon_decimal_point :'.');
284-
if (*lconvert->mon_thousands_sep!='\0')
285-
ssymbol=*lconvert->mon_thousands_sep;
286-
else
287-
/* ssymbol should not equal dsymbol */
288-
ssymbol= (dsymbol!=',') ?',' :'.';
289-
csymbol= ((*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$");
290-
nsymbol= ((*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-");
291-
292-
point_pos=LAST_DIGIT-points;
293288

294-
point_pos-= (points-1) /mon_group;
295-
ssymbol_position=point_pos % (mon_group+1);
289+
/* we restrict dsymbol to be a single byte, but not the other symbols */
290+
if (*lconvert->mon_decimal_point!='\0'&&
291+
lconvert->mon_decimal_point[1]=='\0')
292+
dsymbol=*lconvert->mon_decimal_point;
293+
else
294+
dsymbol='.';
295+
if (*lconvert->mon_thousands_sep!='\0')
296+
ssymbol=lconvert->mon_thousands_sep;
297+
else/* ssymbol should not equal dsymbol */
298+
ssymbol= (dsymbol!=',') ?"," :".";
299+
csymbol= (*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$";
300+
nsymbol= (*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-";
296301

297302
/* we work with positive amounts and add the minus sign at the end */
298303
if (value<0)
299304
{
300-
minus=1;
305+
minus=true;
301306
value=-value;
302307
}
303308

304-
/*allow for trailing negative strings */
305-
MemSet(buf,' ',CASH_BUFSZ);
306-
buf[TERMINATOR]=buf[LAST_PAREN]='\0';
309+
/*we build the result string right-to-left in buf[] */
310+
bufptr=buf+sizeof(buf)-1;
311+
*bufptr='\0';
307312

308-
while (value||count> (point_pos-2))
313+
/*
314+
* Generate digits till there are no non-zero digits left and we emitted
315+
* at least one to the left of the decimal point. digit_pos is the
316+
* current digit position, with zero as the digit just left of the decimal
317+
* point, increasing to the right.
318+
*/
319+
digit_pos=points;
320+
do
309321
{
310-
if (points&&count==point_pos)
311-
buf[count--]=dsymbol;
312-
elseif (ssymbol&&count % (mon_group+1)==ssymbol_position)
313-
buf[count--]=ssymbol;
322+
if (points&&digit_pos==0)
323+
{
324+
/* insert decimal point */
325+
*(--bufptr)=dsymbol;
326+
}
327+
elseif (digit_pos<points&& (digit_pos %mon_group)==0)
328+
{
329+
/* insert thousands sep */
330+
bufptr-=strlen(ssymbol);
331+
memcpy(bufptr,ssymbol,strlen(ssymbol));
332+
}
314333

315-
buf[count--]= ((uint64)value %10)+'0';
334+
*(--bufptr)= ((uint64)value %10)+'0';
316335
value= ((uint64)value) /10;
317-
}
318-
319-
strncpy((buf+count-strlen(csymbol)+1),csymbol,strlen(csymbol));
320-
count-=strlen(csymbol)-1;
336+
digit_pos--;
337+
}while (value||digit_pos >=0);
321338

322-
/*
323-
* If points == 0 and the number of digits % mon_group == 0, the code
324-
* above adds a trailing ssymbol on the far right, so remove it.
325-
*/
326-
if (buf[LAST_DIGIT]==ssymbol)
327-
buf[LAST_DIGIT]='\0';
339+
/* prepend csymbol */
340+
bufptr-=strlen(csymbol);
341+
memcpy(bufptr,csymbol,strlen(csymbol));
328342

329343
/* see if we need to signify negative amount */
330344
if (minus)
331345
{
332-
result=palloc(CASH_BUFSZ+2-count+strlen(nsymbol));
346+
result=palloc(strlen(bufptr)+strlen(nsymbol)+3);
333347

334348
/* Position code of 0 means use parens */
335349
if (convention==0)
336-
sprintf(result,"(%s)",buf+count);
350+
sprintf(result,"(%s)",bufptr);
337351
elseif (convention==2)
338-
sprintf(result,"%s%s",buf+count,nsymbol);
352+
sprintf(result,"%s%s",bufptr,nsymbol);
339353
else
340-
sprintf(result,"%s%s",nsymbol,buf+count);
354+
sprintf(result,"%s%s",nsymbol,bufptr);
341355
}
342356
else
343357
{
344-
result=palloc(CASH_BUFSZ+2-count);
345-
strcpy(result,buf+count);
358+
/* just emit what we have */
359+
result=pstrdup(bufptr);
346360
}
347361

348362
PG_RETURN_CSTRING(result);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp