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

Commit5b297de

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 parent7208e0b commit5b297de

File tree

1 file changed

+112
-89
lines changed

1 file changed

+112
-89
lines changed

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

Lines changed: 112 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@
2626

2727
staticconstchar*num_word(Cashvalue);
2828

29-
/* when we go to 64 bit values we will have to modify this */
30-
#defineCASH_BUFSZ24
31-
32-
#defineTERMINATOR(CASH_BUFSZ - 1)
33-
#defineLAST_PAREN(TERMINATOR - 1)
34-
#defineLAST_DIGIT(LAST_PAREN - 1)
35-
3629

3730
/*
3831
* Cash is a pass-by-ref SQL type, so we must pass and return pointers.
@@ -71,14 +64,14 @@ cash_in(PG_FUNCTION_ARGS)
7164
Cashvalue=0;
7265
Cashdec=0;
7366
Cashsgn=1;
74-
intseen_dot=0;
67+
boolseen_dot=false;
7568
constchar*s=str;
7669
intfpoint;
77-
char*csymbol;
78-
chardsymbol,
79-
ssymbol,
80-
psymbol,
81-
*nsymbol;
70+
chardsymbol;
71+
constchar*ssymbol,
72+
*psymbol,
73+
*nsymbol,
74+
*csymbol;
8275

8376
structlconv*lconvert=PGLC_localeconv();
8477

@@ -96,14 +89,22 @@ cash_in(PG_FUNCTION_ARGS)
9689
if (fpoint<0||fpoint>10)
9790
fpoint=2;/* best guess in this case, I think */
9891

99-
dsymbol= ((*lconvert->mon_decimal_point!='\0') ?*lconvert->mon_decimal_point :'.');
100-
ssymbol= ((*lconvert->mon_thousands_sep!='\0') ?*lconvert->mon_thousands_sep :',');
101-
csymbol= ((*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$");
102-
psymbol= ((*lconvert->positive_sign!='\0') ?*lconvert->positive_sign :'+');
103-
nsymbol= ((*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-");
92+
/* we restrict dsymbol to be a single byte, but not the other symbols */
93+
if (*lconvert->mon_decimal_point!='\0'&&
94+
lconvert->mon_decimal_point[1]=='\0')
95+
dsymbol=*lconvert->mon_decimal_point;
96+
else
97+
dsymbol='.';
98+
if (*lconvert->mon_thousands_sep!='\0')
99+
ssymbol=lconvert->mon_thousands_sep;
100+
else/* ssymbol should not equal dsymbol */
101+
ssymbol= (dsymbol!=',') ?"," :".";
102+
csymbol= (*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$";
103+
psymbol= (*lconvert->positive_sign!='\0') ?lconvert->positive_sign :"+";
104+
nsymbol= (*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-";
104105

105106
#ifdefCASHDEBUG
106-
printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n",
107+
printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
107108
fpoint,dsymbol,ssymbol,csymbol,psymbol,nsymbol);
108109
#endif
109110

@@ -124,23 +125,20 @@ cash_in(PG_FUNCTION_ARGS)
124125
{
125126
sgn=-1;
126127
s+=strlen(nsymbol);
127-
#ifdefCASHDEBUG
128-
printf("cashin- negative symbol; string is '%s'\n",s);
129-
#endif
130128
}
131129
elseif (*s=='(')
132130
{
133131
sgn=-1;
134132
s++;
135-
136133
}
137-
elseif (*s==psymbol)
138-
s++;
134+
elseif (strncmp(s,psymbol,strlen(psymbol))==0)
135+
s+=strlen(psymbol);
139136

140137
#ifdefCASHDEBUG
141138
printf("cashin- string is '%s'\n",s);
142139
#endif
143140

141+
/* allow whitespace and currency symbol after the sign, too */
144142
while (isspace((unsignedchar)*s))
145143
s++;
146144
if (strncmp(s,csymbol,strlen(csymbol))==0)
@@ -150,7 +148,7 @@ cash_in(PG_FUNCTION_ARGS)
150148
printf("cashin- string is '%s'\n",s);
151149
#endif
152150

153-
for (;;s++)
151+
for (;*s;s++)
154152
{
155153
/* we look for digits as long as we have found less */
156154
/* than the required number of decimal places */
@@ -164,30 +162,44 @@ cash_in(PG_FUNCTION_ARGS)
164162
/* decimal point? then start counting fractions... */
165163
elseif (*s==dsymbol&& !seen_dot)
166164
{
167-
seen_dot=1;
165+
seen_dot=true;
168166
}
169167
/* ignore if "thousands" separator, else we're done */
170-
elseif (*s!=ssymbol)
171-
{
172-
/* round off */
173-
if (isdigit((unsignedchar)*s)&&*s >='5')
174-
value++;
175-
176-
/* adjust for less than required decimal places */
177-
for (;dec<fpoint;dec++)
178-
value *=10;
179-
168+
elseif (strncmp(s,ssymbol,strlen(ssymbol))==0)
169+
s+=strlen(ssymbol)-1;
170+
else
180171
break;
181-
}
182172
}
183173

184-
while (isspace((unsignedchar)*s)||*s=='0'||*s==')')
185-
s++;
174+
/* round off if there's another digit */
175+
if (isdigit((unsignedchar)*s)&&*s >='5')
176+
value++;
186177

187-
if (*s!='\0')
188-
ereport(ERROR,
189-
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
190-
errmsg("invalid input syntax for type money: \"%s\"",str)));
178+
/* adjust for less than required decimal places */
179+
for (;dec<fpoint;dec++)
180+
value *=10;
181+
182+
/*
183+
* should only be trailing digits followed by whitespace, right paren,
184+
* or possibly a trailing minus sign
185+
*/
186+
while (isdigit((unsignedchar)*s))
187+
s++;
188+
while (*s)
189+
{
190+
if (isspace((unsignedchar)*s)||*s==')')
191+
s++;
192+
elseif (strncmp(s,nsymbol,strlen(nsymbol))==0)
193+
{
194+
sgn=-1;
195+
s+=strlen(nsymbol);
196+
}
197+
else
198+
ereport(ERROR,
199+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
200+
errmsg("invalid input syntax for type money: \"%s\"",
201+
str)));
202+
}
191203

192204
result=value*sgn;
193205

@@ -200,25 +212,23 @@ cash_in(PG_FUNCTION_ARGS)
200212

201213

202214
/* cash_out()
203-
* Function to convert cash to a dollars and cents representation.
204-
* XXX HACK This code appears to assume US conventions for
205-
*positive-valued amounts. - tgl 97/04/14
215+
* Function to convert cash to a dollars and cents representation, using
216+
* the lc_monetary locale's formatting.
206217
*/
207218
Datum
208219
cash_out(PG_FUNCTION_ARGS)
209220
{
210221
Cashvalue=PG_GETARG_CASH(0);
211222
char*result;
212-
charbuf[CASH_BUFSZ];
213-
intminus=0;
214-
intcount=LAST_DIGIT;
215-
intpoint_pos;
216-
intcomma_position=0;
223+
charbuf[128];
224+
char*bufptr;
225+
boolminus= false;
226+
intdigit_pos;
217227
intpoints,
218228
mon_group;
219-
charcomma;
220-
char*csymbol,
221-
dsymbol,
229+
chardsymbol;
230+
constchar*ssymbol,
231+
*csymbol,
222232
*nsymbol;
223233
charconvention;
224234

@@ -237,66 +247,79 @@ cash_out(PG_FUNCTION_ARGS)
237247
if (mon_group <=0||mon_group>6)
238248
mon_group=3;
239249

240-
comma= ((*lconvert->mon_thousands_sep!='\0') ?*lconvert->mon_thousands_sep :',');
241250
convention=lconvert->n_sign_posn;
242-
dsymbol= ((*lconvert->mon_decimal_point!='\0') ?*lconvert->mon_decimal_point :'.');
243-
csymbol= ((*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$");
244-
nsymbol= ((*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-");
245-
246-
point_pos=LAST_DIGIT-points;
247251

248-
/* allow more than three decimal points and separate them */
249-
if (comma)
250-
{
251-
point_pos-= (points-1) /mon_group;
252-
comma_position=point_pos % (mon_group+1);
253-
}
252+
/* we restrict dsymbol to be a single byte, but not the other symbols */
253+
if (*lconvert->mon_decimal_point!='\0'&&
254+
lconvert->mon_decimal_point[1]=='\0')
255+
dsymbol=*lconvert->mon_decimal_point;
256+
else
257+
dsymbol='.';
258+
if (*lconvert->mon_thousands_sep!='\0')
259+
ssymbol=lconvert->mon_thousands_sep;
260+
else/* ssymbol should not equal dsymbol */
261+
ssymbol= (dsymbol!=',') ?"," :".";
262+
csymbol= (*lconvert->currency_symbol!='\0') ?lconvert->currency_symbol :"$";
263+
nsymbol= (*lconvert->negative_sign!='\0') ?lconvert->negative_sign :"-";
254264

255265
/* we work with positive amounts and add the minus sign at the end */
256266
if (value<0)
257267
{
258-
minus=1;
268+
minus=true;
259269
value=-value;
260270
}
261271

262-
/*allow for trailing negative strings */
263-
MemSet(buf,' ',CASH_BUFSZ);
264-
buf[TERMINATOR]=buf[LAST_PAREN]='\0';
272+
/*we build the result string right-to-left in buf[] */
273+
bufptr=buf+sizeof(buf)-1;
274+
*bufptr='\0';
265275

266-
while (value||count> (point_pos-2))
276+
/*
277+
* Generate digits till there are no non-zero digits left and we emitted
278+
* at least one to the left of the decimal point. digit_pos is the
279+
* current digit position, with zero as the digit just left of the decimal
280+
* point, increasing to the right.
281+
*/
282+
digit_pos=points;
283+
do
267284
{
268-
if (points&&count==point_pos)
269-
buf[count--]=dsymbol;
270-
elseif (comma&&count % (mon_group+1)==comma_position)
271-
buf[count--]=comma;
285+
if (points&&digit_pos==0)
286+
{
287+
/* insert decimal point */
288+
*(--bufptr)=dsymbol;
289+
}
290+
elseif (digit_pos<points&& (digit_pos %mon_group)==0)
291+
{
292+
/* insert thousands sep */
293+
bufptr-=strlen(ssymbol);
294+
memcpy(bufptr,ssymbol,strlen(ssymbol));
295+
}
272296

273-
buf[count--]= ((unsignedint)value %10)+'0';
297+
*(--bufptr)= ((unsignedint)value %10)+'0';
274298
value= ((unsignedint)value) /10;
275-
}
276-
277-
strncpy((buf+count-strlen(csymbol)+1),csymbol,strlen(csymbol));
278-
count-=strlen(csymbol)-1;
299+
digit_pos--;
300+
}while (value||digit_pos >=0);
279301

280-
if (buf[LAST_DIGIT]==',')
281-
buf[LAST_DIGIT]=buf[LAST_PAREN];
302+
/* prepend csymbol */
303+
bufptr-=strlen(csymbol);
304+
memcpy(bufptr,csymbol,strlen(csymbol));
282305

283306
/* see if we need to signify negative amount */
284307
if (minus)
285308
{
286-
result=palloc(CASH_BUFSZ+2-count+strlen(nsymbol));
309+
result=palloc(strlen(bufptr)+strlen(nsymbol)+3);
287310

288311
/* Position code of 0 means use parens */
289312
if (convention==0)
290-
sprintf(result,"(%s)",buf+count);
313+
sprintf(result,"(%s)",bufptr);
291314
elseif (convention==2)
292-
sprintf(result,"%s%s",buf+count,nsymbol);
315+
sprintf(result,"%s%s",bufptr,nsymbol);
293316
else
294-
sprintf(result,"%s%s",nsymbol,buf+count);
317+
sprintf(result,"%s%s",nsymbol,bufptr);
295318
}
296319
else
297320
{
298-
result=palloc(CASH_BUFSZ+2-count);
299-
strcpy(result,buf+count);
321+
/* just emit what we have */
322+
result=pstrdup(bufptr);
300323
}
301324

302325
PG_RETURN_CSTRING(result);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp