Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3147acd

Browse files
committed
Use improved vsnprintf calling logic in more places.
When we are using a C99-compliant vsnprintf implementation (which should be most places, these days) it is worth the trouble to make use of its report of how large the buffer needs to be to succeed. This patch adjusts stringinfo.c and some miscellaneous usages in pg_dump to do that, relying on the logic recently added in libpgcommon's psprintf.c. Since these places want to know the number of bytes written once we succeed, modify the API of pvsnprintf() to report that. There remains near-duplicate logic in pqexpbuffer.c, but since that code is in libpq, psprintf.c's approach of exit()-on-error isn't appropriate for use there. Also note that I didn't bother touching the multitude of places that call (v)snprintf without any attempt to provide a resizable buffer. Release-note-worthy incompatibility: the API of appendStringInfoVA() changed. If there's any third-party code that's calling that directly, it will need tweaking along the same lines as in this patch. David Rowley and Tom Lane
1 parent 98c5065 commit 3147acd

File tree

8 files changed

+153
-137
lines changed

8 files changed

+153
-137
lines changed

src/backend/lib/stringinfo.c

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,18 @@ appendStringInfo(StringInfo str, const char *fmt,...)
8080
for (;;)
8181
{
8282
va_list args;
83-
bool success;
83+
int needed;
8484

8585
/* Try to format the data. */
8686
va_start(args, fmt);
87-
success = appendStringInfoVA(str, fmt, args);
87+
needed = appendStringInfoVA(str, fmt, args);
8888
va_end(args);
8989

90-
if (success)
91-
break;
90+
if (needed == 0)
91+
break; /* success */
9292

93-
/* Double the buffer size and try again. */
94-
enlargeStringInfo(str, str->maxlen);
93+
/* Increase the buffer size and try again. */
94+
enlargeStringInfo(str, needed);
9595
}
9696
}
9797

@@ -100,59 +100,51 @@ appendStringInfo(StringInfo str, const char *fmt,...)
100100
*
101101
* Attempt to format text data under the control of fmt (an sprintf-style
102102
* format string) and append it to whatever is already in str. If successful
103-
* return true; if not (because there's not enough space), return false
104-
* without modifying str. Typically the caller would enlarge str and retry
105-
* on false return --- see appendStringInfo for standard usage pattern.
103+
* return zero; if not (because there's not enough space), return an estimate
104+
* of the space needed, without modifying str. Typically the caller should
105+
* pass the return value to enlargeStringInfo() before trying again; see
106+
* appendStringInfo for standard usage pattern.
106107
*
107108
* XXX This API is ugly, but there seems no alternative given the C spec's
108109
* restrictions on what can portably be done with va_list arguments: you have
109110
* to redo va_start before you can rescan the argument list, and we can't do
110111
* that from here.
111112
*/
112-
bool
113+
int
113114
appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
114115
{
115-
int avail,
116-
nprinted;
116+
int avail;
117+
size_t nprinted;
117118

118119
Assert(str != NULL);
119120

120121
/*
121122
* If there's hardly any space, don't bother trying, just fail to make the
122-
* caller enlarge the buffer first.
123+
* caller enlarge the buffer first. We have to guess at how much to
124+
* enlarge, since we're skipping the formatting work.
123125
*/
124-
avail = str->maxlen - str->len - 1;
126+
avail = str->maxlen - str->len;
125127
if (avail < 16)
126-
return false;
128+
return 32;
127129

128-
/*
129-
* Assert check here is to catch buggy vsnprintf that overruns the
130-
* specified buffer length. Solaris 7 in 64-bit mode is an example of a
131-
* platform with such a bug.
132-
*/
133-
#ifdef USE_ASSERT_CHECKING
134-
str->data[str->maxlen - 1] = '\0';
135-
#endif
136-
137-
nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
130+
nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
138131

139-
Assert(str->data[str->maxlen - 1] == '\0');
140-
141-
/*
142-
* Note: some versions of vsnprintf return the number of chars actually
143-
* stored, but at least one returns -1 on failure. Be conservative about
144-
* believing whether the print worked.
145-
*/
146-
if (nprinted >= 0 && nprinted < avail - 1)
132+
if (nprinted < (size_t) avail)
147133
{
148134
/* Success. Note nprinted does not include trailing null. */
149-
str->len += nprinted;
150-
return true;
135+
str->len += (int) nprinted;
136+
return 0;
151137
}
152138

153139
/* Restore the trailing null so that str is unmodified. */
154140
str->data[str->len] = '\0';
155-
return false;
141+
142+
/*
143+
* Return pvsnprintf's estimate of the space needed. (Although this is
144+
* given as a size_t, we know it will fit in int because it's not more
145+
* than MaxAllocSize.)
146+
*/
147+
return (int) nprinted;
156148
}
157149

158150
/*

src/backend/utils/error/elog.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -715,13 +715,13 @@ errcode_for_socket_access(void)
715715
for (;;) \
716716
{ \
717717
va_list args; \
718-
bool success; \
718+
int needed; \
719719
va_start(args, fmt); \
720-
success = appendStringInfoVA(&buf, fmtbuf, args); \
720+
needed = appendStringInfoVA(&buf, fmtbuf, args); \
721721
va_end(args); \
722-
if (success) \
722+
if (needed == 0) \
723723
break; \
724-
enlargeStringInfo(&buf, buf.maxlen); \
724+
enlargeStringInfo(&buf, needed); \
725725
} \
726726
/* Done with expanded fmt */ \
727727
pfree(fmtbuf); \
@@ -758,13 +758,13 @@ errcode_for_socket_access(void)
758758
for (;;) \
759759
{ \
760760
va_list args; \
761-
bool success; \
761+
int needed; \
762762
va_start(args, n); \
763-
success = appendStringInfoVA(&buf, fmtbuf, args); \
763+
needed = appendStringInfoVA(&buf, fmtbuf, args); \
764764
va_end(args); \
765-
if (success) \
765+
if (needed == 0) \
766766
break; \
767-
enlargeStringInfo(&buf, buf.maxlen); \
767+
enlargeStringInfo(&buf, needed); \
768768
} \
769769
/* Done with expanded fmt */ \
770770
pfree(fmtbuf); \

src/bin/pg_dump/pg_backup_archiver.c

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,29 +1183,33 @@ archputs(const char *s, Archive *AH)
11831183
int
11841184
archprintf(Archive *AH, const char *fmt,...)
11851185
{
1186-
char *p = NULL;
1187-
va_list ap;
1188-
int bSize = strlen(fmt) + 256;
1189-
int cnt = -1;
1186+
char *p;
1187+
size_t len = 128; /* initial assumption about buffer size */
1188+
size_t cnt;
11901189

1191-
/*
1192-
* This is paranoid: deal with the possibility that vsnprintf is willing
1193-
* to ignore trailing null or returns > 0 even if string does not fit. It
1194-
* may be the case that it returns cnt = bufsize
1195-
*/
1196-
while (cnt < 0 || cnt >= (bSize - 1))
1190+
for (;;)
11971191
{
1198-
if (p != NULL)
1199-
free(p);
1200-
bSize *= 2;
1201-
p = (char *) pg_malloc(bSize);
1202-
va_start(ap, fmt);
1203-
cnt = vsnprintf(p, bSize, fmt, ap);
1204-
va_end(ap);
1192+
va_list args;
1193+
1194+
/* Allocate work buffer. */
1195+
p = (char *) pg_malloc(len);
1196+
1197+
/* Try to format the data. */
1198+
va_start(args, fmt);
1199+
cnt = pvsnprintf(p, len, fmt, args);
1200+
va_end(args);
1201+
1202+
if (cnt < len)
1203+
break; /* success */
1204+
1205+
/* Release buffer and loop around to try again with larger len. */
1206+
free(p);
1207+
len = cnt;
12051208
}
1209+
12061210
WriteData(AH, p, cnt);
12071211
free(p);
1208-
return cnt;
1212+
return (int) cnt;
12091213
}
12101214

12111215

@@ -1312,29 +1316,33 @@ RestoreOutput(ArchiveHandle *AH, OutputContext savedContext)
13121316
int
13131317
ahprintf(ArchiveHandle *AH, const char *fmt,...)
13141318
{
1315-
char *p = NULL;
1316-
va_list ap;
1317-
int bSize = strlen(fmt) + 256; /* Usually enough */
1318-
int cnt = -1;
1319+
char *p;
1320+
size_t len = 128; /* initial assumption about buffer size */
1321+
size_t cnt;
13191322

1320-
/*
1321-
* This is paranoid: deal with the possibility that vsnprintf is willing
1322-
* to ignore trailing null or returns > 0 even if string does not fit. It
1323-
* may be the case that it returns cnt = bufsize.
1324-
*/
1325-
while (cnt < 0 || cnt >= (bSize - 1))
1323+
for (;;)
13261324
{
1327-
if (p != NULL)
1328-
free(p);
1329-
bSize *= 2;
1330-
p = (char *) pg_malloc(bSize);
1331-
va_start(ap, fmt);
1332-
cnt = vsnprintf(p, bSize, fmt, ap);
1333-
va_end(ap);
1325+
va_list args;
1326+
1327+
/* Allocate work buffer. */
1328+
p = (char *) pg_malloc(len);
1329+
1330+
/* Try to format the data. */
1331+
va_start(args, fmt);
1332+
cnt = pvsnprintf(p, len, fmt, args);
1333+
va_end(args);
1334+
1335+
if (cnt < len)
1336+
break; /* success */
1337+
1338+
/* Release buffer and loop around to try again with larger len. */
1339+
free(p);
1340+
len = cnt;
13341341
}
1342+
13351343
ahwrite(p, 1, cnt, AH);
13361344
free(p);
1337-
return cnt;
1345+
return (int) cnt;
13381346
}
13391347

13401348
void

src/bin/pg_dump/pg_backup_tar.c

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -996,33 +996,33 @@ _EndBlobs(ArchiveHandle *AH, TocEntry *te)
996996
static int
997997
tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
998998
{
999-
char *p = NULL;
1000-
va_list ap;
1001-
size_t bSize = strlen(fmt) + 256; /* Should be enough */
1002-
int cnt = -1;
1003-
1004-
/*
1005-
* This is paranoid: deal with the possibility that vsnprintf is willing
1006-
* to ignore trailing null
1007-
*/
999+
char *p;
1000+
size_t len = 128; /* initial assumption about buffer size */
1001+
size_t cnt;
10081002

1009-
/*
1010-
* or returns > 0 even if string does not fit. It may be the case that it
1011-
* returns cnt = bufsize
1012-
*/
1013-
while (cnt < 0 || cnt >= (bSize - 1))
1003+
for (;;)
10141004
{
1015-
if (p != NULL)
1016-
free(p);
1017-
bSize *= 2;
1018-
p = (char *) pg_malloc(bSize);
1019-
va_start(ap, fmt);
1020-
cnt = vsnprintf(p, bSize, fmt, ap);
1021-
va_end(ap);
1005+
va_list args;
1006+
1007+
/* Allocate work buffer. */
1008+
p = (char *) pg_malloc(len);
1009+
1010+
/* Try to format the data. */
1011+
va_start(args, fmt);
1012+
cnt = pvsnprintf(p, len, fmt, args);
1013+
va_end(args);
1014+
1015+
if (cnt < len)
1016+
break; /* success */
1017+
1018+
/* Release buffer and loop around to try again with larger len. */
1019+
free(p);
1020+
len = cnt;
10221021
}
1022+
10231023
cnt = tarWrite(p, cnt, th);
10241024
free(p);
1025-
return cnt;
1025+
return (int) cnt;
10261026
}
10271027

10281028
bool

0 commit comments

Comments
 (0)