Skip to content

Commit 5bf948d

Browse files
committed
Replace static bufs with a StringInfo in cash_words()
For clarity. The code was correct, and the buffer was large enough, but string manipulation with no bounds checking is scary. This incurs an extra palloc+pfree to every call, but in quick performance testing, it doesn't seem to be significant. Reviewed-by: Robert Haas Discussion: https://www.postgresql.org/message-id/7f86e06a-98c5-4ce3-8ec9-3885c8de0358@iki.fi
1 parent 47c9803 commit 5bf948d

File tree

1 file changed

+44
-41
lines changed

1 file changed

+44
-41
lines changed

src/backend/utils/adt/cash.c

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535
* Private routines
3636
************************************************************************/
3737

38-
static const char *
39-
num_word(Cash value)
38+
static void
39+
append_num_word(StringInfo buf, Cash value)
4040
{
41-
static char buf[128];
4241
static const char *const small[] = {
4342
"zero", "one", "two", "three", "four", "five", "six", "seven",
4443
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
@@ -50,41 +49,42 @@ num_word(Cash value)
5049

5150
/* deal with the simple cases first */
5251
if (value <= 20)
53-
return small[value];
52+
{
53+
appendStringInfoString(buf, small[value]);
54+
return;
55+
}
5456

5557
/* is it an even multiple of 100? */
5658
if (!tu)
5759
{
58-
sprintf(buf, "%s hundred", small[value / 100]);
59-
return buf;
60+
appendStringInfo(buf, "%s hundred", small[value / 100]);
61+
return;
6062
}
6163

6264
/* more than 99? */
6365
if (value > 99)
6466
{
6567
/* is it an even multiple of 10 other than 10? */
6668
if (value % 10 == 0 && tu > 10)
67-
sprintf(buf, "%s hundred %s",
68-
small[value / 100], big[tu / 10]);
69+
appendStringInfo(buf, "%s hundred %s",
70+
small[value / 100], big[tu / 10]);
6971
else if (tu < 20)
70-
sprintf(buf, "%s hundred and %s",
71-
small[value / 100], small[tu]);
72+
appendStringInfo(buf, "%s hundred and %s",
73+
small[value / 100], small[tu]);
7274
else
73-
sprintf(buf, "%s hundred %s %s",
74-
small[value / 100], big[tu / 10], small[tu % 10]);
75+
appendStringInfo(buf, "%s hundred %s %s",
76+
small[value / 100], big[tu / 10], small[tu % 10]);
7577
}
7678
else
7779
{
7880
/* is it an even multiple of 10 other than 10? */
7981
if (value % 10 == 0 && tu > 10)
80-
sprintf(buf, "%s", big[tu / 10]);
82+
appendStringInfoString(buf, big[tu / 10]);
8183
else if (tu < 20)
82-
sprintf(buf, "%s", small[tu]);
84+
appendStringInfoString(buf, small[tu]);
8385
else
84-
sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
86+
appendStringInfo(buf, "%s %s", big[tu / 10], small[tu % 10]);
8587
}
86-
87-
return buf;
8888
} /* num_word() */
8989

9090
static inline Cash
@@ -960,8 +960,9 @@ cash_words(PG_FUNCTION_ARGS)
960960
{
961961
Cash value = PG_GETARG_CASH(0);
962962
uint64 val;
963-
char buf[256];
964-
char *p = buf;
963+
StringInfoData buf;
964+
text *res;
965+
Cash dollars;
965966
Cash m0;
966967
Cash m1;
967968
Cash m2;
@@ -970,19 +971,19 @@ cash_words(PG_FUNCTION_ARGS)
970971
Cash m5;
971972
Cash m6;
972973

974+
initStringInfo(&buf);
975+
973976
/* work with positive numbers */
974977
if (value < 0)
975978
{
976979
value = -value;
977-
strcpy(buf, "minus ");
978-
p += 6;
980+
appendStringInfoString(&buf, "minus ");
979981
}
980-
else
981-
buf[0] = '\0';
982982

983983
/* Now treat as unsigned, to avoid trouble at INT_MIN */
984984
val = (uint64) value;
985985

986+
dollars = val / INT64CONST(100);
986987
m0 = val % INT64CONST(100); /* cents */
987988
m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
988989
m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
@@ -993,49 +994,51 @@ cash_words(PG_FUNCTION_ARGS)
993994

994995
if (m6)
995996
{
996-
strcat(buf, num_word(m6));
997-
strcat(buf, " quadrillion ");
997+
append_num_word(&buf, m6);
998+
appendStringInfoString(&buf, " quadrillion ");
998999
}
9991000

10001001
if (m5)
10011002
{
1002-
strcat(buf, num_word(m5));
1003-
strcat(buf, " trillion ");
1003+
append_num_word(&buf, m5);
1004+
appendStringInfoString(&buf, " trillion ");
10041005
}
10051006

10061007
if (m4)
10071008
{
1008-
strcat(buf, num_word(m4));
1009-
strcat(buf, " billion ");
1009+
append_num_word(&buf, m4);
1010+
appendStringInfoString(&buf, " billion ");
10101011
}
10111012

10121013
if (m3)
10131014
{
1014-
strcat(buf, num_word(m3));
1015-
strcat(buf, " million ");
1015+
append_num_word(&buf, m3);
1016+
appendStringInfoString(&buf, " million ");
10161017
}
10171018

10181019
if (m2)
10191020
{
1020-
strcat(buf, num_word(m2));
1021-
strcat(buf, " thousand ");
1021+
append_num_word(&buf, m2);
1022+
appendStringInfoString(&buf, " thousand ");
10221023
}
10231024

10241025
if (m1)
1025-
strcat(buf, num_word(m1));
1026+
append_num_word(&buf, m1);
10261027

1027-
if (!*p)
1028-
strcat(buf, "zero");
1028+
if (dollars == 0)
1029+
appendStringInfoString(&buf, "zero");
10291030

1030-
strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
1031-
strcat(buf, num_word(m0));
1032-
strcat(buf, m0 == 1 ? " cent" : " cents");
1031+
appendStringInfoString(&buf, dollars == 1 ? " dollar and " : " dollars and ");
1032+
append_num_word(&buf, m0);
1033+
appendStringInfoString(&buf, m0 == 1 ? " cent" : " cents");
10331034

10341035
/* capitalize output */
1035-
buf[0] = pg_toupper((unsigned char) buf[0]);
1036+
buf.data[0] = pg_toupper((unsigned char) buf.data[0]);
10361037

10371038
/* return as text datum */
1038-
PG_RETURN_TEXT_P(cstring_to_text(buf));
1039+
res = cstring_to_text_with_len(buf.data, buf.len);
1040+
pfree(buf.data);
1041+
PG_RETURN_TEXT_P(res);
10391042
}
10401043

10411044

0 commit comments

Comments
 (0)