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

Commit7609239

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 parent78d523b commit7609239

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
@@ -30,12 +30,6 @@
3030
#include"utils/numeric.h"
3131
#include"utils/pg_locale.h"
3232

33-
#defineCASH_BUFSZ36
34-
35-
#defineTERMINATOR(CASH_BUFSZ - 1)
36-
#defineLAST_PAREN(TERMINATOR - 1)
37-
#defineLAST_DIGIT(LAST_PAREN - 1)
38-
3933

4034
/*************************************************************************
4135
* Private routines
@@ -107,13 +101,13 @@ cash_in(PG_FUNCTION_ARGS)
107101
Cashvalue=0;
108102
Cashdec=0;
109103
Cashsgn=1;
110-
intseen_dot=0;
104+
boolseen_dot=false;
111105
constchar*s=str;
112106
intfpoint;
113-
chardsymbol,
114-
ssymbol,
115-
psymbol;
116-
constchar*nsymbol,
107+
chardsymbol;
108+
constchar*ssymbol,
109+
*psymbol,
110+
*nsymbol,
117111
*csymbol;
118112
structlconv*lconvert=PGLC_localeconv();
119113

@@ -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
structlconv*lconvert=PGLC_localeconv();
267272

@@ -279,69 +284,78 @@ cash_out(PG_FUNCTION_ARGS)
279284
mon_group=3;
280285

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

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

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

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

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

314-
buf[count--]= ((uint64)value %10)+'0';
333+
*(--bufptr)= ((uint64)value %10)+'0';
315334
value= ((uint64)value) /10;
316-
}
317-
318-
strncpy((buf+count-strlen(csymbol)+1),csymbol,strlen(csymbol));
319-
count-=strlen(csymbol)-1;
335+
digit_pos--;
336+
}while (value||digit_pos >=0);
320337

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

328342
/* see if we need to signify negative amount */
329343
if (minus)
330344
{
331-
result=palloc(CASH_BUFSZ+2-count+strlen(nsymbol));
345+
result=palloc(strlen(bufptr)+strlen(nsymbol)+3);
332346

333347
/* Position code of 0 means use parens */
334348
if (convention==0)
335-
sprintf(result,"(%s)",buf+count);
349+
sprintf(result,"(%s)",bufptr);
336350
elseif (convention==2)
337-
sprintf(result,"%s%s",buf+count,nsymbol);
351+
sprintf(result,"%s%s",bufptr,nsymbol);
338352
else
339-
sprintf(result,"%s%s",nsymbol,buf+count);
353+
sprintf(result,"%s%s",nsymbol,bufptr);
340354
}
341355
else
342356
{
343-
result=palloc(CASH_BUFSZ+2-count);
344-
strcpy(result,buf+count);
357+
/* just emit what we have */
358+
result=pstrdup(bufptr);
345359
}
346360

347361
PG_RETURN_CSTRING(result);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp