PostgreSQL Source Code  git master
pqexpbuffer.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pqexpbuffer.c
4  *
5  * PQExpBuffer provides an indefinitely-extensible string data type.
6  * It can be used to buffer either ordinary C strings (null-terminated text)
7  * or arbitrary binary data. All storage is allocated with malloc().
8  *
9  * This module is essentially the same as the backend's StringInfo data type,
10  * but it is intended for use in frontend libpq and client applications.
11  * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
12  * will exit() on error.
13  *
14  * It does rely on vsnprintf(); if configure finds that libc doesn't provide
15  * a usable vsnprintf(), then a copy of our own implementation of it will
16  * be linked into libpq.
17  *
18  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
19  * Portions Copyright (c) 1994, Regents of the University of California
20  *
21  * src/interfaces/libpq/pqexpbuffer.c
22  *
23  *-------------------------------------------------------------------------
24  */
25 
26 #include "postgres_fe.h"
27 
28 #include <limits.h>
29 
30 #include "pqexpbuffer.h"
31 
32 #ifdef WIN32
33 #include "win32.h"
34 #endif
35 
36 
37 /* All "broken" PQExpBuffers point to this string. */
38 static const char oom_buffer[1] = "";
39 
40 /* Need a char * for unconstify() compatibility */
41 static const char *oom_buffer_ptr = oom_buffer;
42 
43 static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2, 0);
44 
45 
46 /*
47  * markPQExpBufferBroken
48  *
49  * Put a PQExpBuffer in "broken" state if it isn't already.
50  */
51 static void
53 {
54  if (str->data != oom_buffer)
55  free(str->data);
56 
57  /*
58  * Casting away const here is a bit ugly, but it seems preferable to not
59  * marking oom_buffer const. We want to do that to encourage the compiler
60  * to put oom_buffer in read-only storage, so that anyone who tries to
61  * scribble on a broken PQExpBuffer will get a failure.
62  */
63  str->data = unconstify(char *, oom_buffer_ptr);
64  str->len = 0;
65  str->maxlen = 0;
66 }
67 
68 /*
69  * createPQExpBuffer
70  *
71  * Create an empty 'PQExpBufferData' & return a pointer to it.
72  */
75 {
76  PQExpBuffer res;
77 
78  res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
79  if (res != NULL)
80  initPQExpBuffer(res);
81 
82  return res;
83 }
84 
85 /*
86  * initPQExpBuffer
87  *
88  * Initialize a PQExpBufferData struct (with previously undefined contents)
89  * to describe an empty string.
90  */
91 void
93 {
94  str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
95  if (str->data == NULL)
96  {
97  str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
98  str->maxlen = 0;
99  str->len = 0;
100  }
101  else
102  {
104  str->len = 0;
105  str->data[0] = '\0';
106  }
107 }
108 
109 /*
110  * destroyPQExpBuffer(str);
111  *
112  * free()s both the data buffer and the PQExpBufferData.
113  * This is the inverse of createPQExpBuffer().
114  */
115 void
117 {
118  if (str)
119  {
120  termPQExpBuffer(str);
121  free(str);
122  }
123 }
124 
125 /*
126  * termPQExpBuffer(str)
127  * free()s the data buffer but not the PQExpBufferData itself.
128  * This is the inverse of initPQExpBuffer().
129  */
130 void
132 {
133  if (str->data != oom_buffer)
134  free(str->data);
135  /* just for luck, make the buffer validly empty. */
136  str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
137  str->maxlen = 0;
138  str->len = 0;
139 }
140 
141 /*
142  * resetPQExpBuffer
143  * Reset a PQExpBuffer to empty
144  *
145  * Note: if possible, a "broken" PQExpBuffer is returned to normal.
146  */
147 void
149 {
150  if (str)
151  {
152  if (str->data != oom_buffer)
153  {
154  str->len = 0;
155  str->data[0] = '\0';
156  }
157  else
158  {
159  /* try to reinitialize to valid state */
160  initPQExpBuffer(str);
161  }
162  }
163 }
164 
165 /*
166  * enlargePQExpBuffer
167  * Make sure there is enough space for 'needed' more bytes in the buffer
168  * ('needed' does not include the terminating null).
169  *
170  * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
171  * the buffer is left in "broken" state.)
172  */
173 int
175 {
176  size_t newlen;
177  char *newdata;
178 
179  if (PQExpBufferBroken(str))
180  return 0; /* already failed */
181 
182  /*
183  * Guard against ridiculous "needed" values, which can occur if we're fed
184  * bogus data. Without this, we can get an overflow or infinite loop in
185  * the following.
186  */
187  if (needed >= ((size_t) INT_MAX - str->len))
188  {
190  return 0;
191  }
192 
193  needed += str->len + 1; /* total space required now */
194 
195  /* Because of the above test, we now have needed <= INT_MAX */
196 
197  if (needed <= str->maxlen)
198  return 1; /* got enough space already */
199 
200  /*
201  * We don't want to allocate just a little more space with each append;
202  * for efficiency, double the buffer size each time it overflows.
203  * Actually, we might need to more than double it if 'needed' is big...
204  */
205  newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
206  while (needed > newlen)
207  newlen = 2 * newlen;
208 
209  /*
210  * Clamp to INT_MAX in case we went past it. Note we are assuming here
211  * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We
212  * will still have newlen >= needed.
213  */
214  if (newlen > (size_t) INT_MAX)
215  newlen = (size_t) INT_MAX;
216 
217  newdata = (char *) realloc(str->data, newlen);
218  if (newdata != NULL)
219  {
220  str->data = newdata;
221  str->maxlen = newlen;
222  return 1;
223  }
224 
226  return 0;
227 }
228 
229 /*
230  * printfPQExpBuffer
231  * Format text data under the control of fmt (an sprintf-like format string)
232  * and insert it into str. More space is allocated to str if necessary.
233  * This is a convenience routine that does the same thing as
234  * resetPQExpBuffer() followed by appendPQExpBuffer().
235  */
236 void
237 printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
238 {
239  int save_errno = errno;
240  va_list args;
241  bool done;
242 
243  resetPQExpBuffer(str);
244 
245  if (PQExpBufferBroken(str))
246  return; /* already failed */
247 
248  /* Loop in case we have to retry after enlarging the buffer. */
249  do
250  {
251  errno = save_errno;
252  va_start(args, fmt);
253  done = appendPQExpBufferVA(str, fmt, args);
254  va_end(args);
255  } while (!done);
256 }
257 
258 /*
259  * appendPQExpBuffer
260  *
261  * Format text data under the control of fmt (an sprintf-like format string)
262  * and append it to whatever is already in str. More space is allocated
263  * to str if necessary. This is sort of like a combination of sprintf and
264  * strcat.
265  */
266 void
267 appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
268 {
269  int save_errno = errno;
270  va_list args;
271  bool done;
272 
273  if (PQExpBufferBroken(str))
274  return; /* already failed */
275 
276  /* Loop in case we have to retry after enlarging the buffer. */
277  do
278  {
279  errno = save_errno;
280  va_start(args, fmt);
281  done = appendPQExpBufferVA(str, fmt, args);
282  va_end(args);
283  } while (!done);
284 }
285 
286 /*
287  * appendPQExpBufferVA
288  * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
289  * Attempt to format data and append it to str. Returns true if done
290  * (either successful or hard failure), false if need to retry.
291  *
292  * Caution: callers must be sure to preserve their entry-time errno
293  * when looping, in case the fmt contains "%m".
294  */
295 static bool
296 appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
297 {
298  size_t avail;
299  size_t needed;
300  int nprinted;
301 
302  /*
303  * Try to format the given string into the available space; but if there's
304  * hardly any space, don't bother trying, just enlarge the buffer first.
305  */
306  if (str->maxlen > str->len + 16)
307  {
308  avail = str->maxlen - str->len;
309 
310  nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
311 
312  /*
313  * If vsnprintf reports an error, fail (we assume this means there's
314  * something wrong with the format string).
315  */
316  if (unlikely(nprinted < 0))
317  {
319  return true;
320  }
321 
322  if ((size_t) nprinted < avail)
323  {
324  /* Success. Note nprinted does not include trailing null. */
325  str->len += nprinted;
326  return true;
327  }
328 
329  /*
330  * We assume a C99-compliant vsnprintf, so believe its estimate of the
331  * required space, and add one for the trailing null. (If it's wrong,
332  * the logic will still work, but we may loop multiple times.)
333  *
334  * Choke if the required space would exceed INT_MAX, since str->maxlen
335  * can't represent more than that.
336  */
337  if (unlikely(nprinted > INT_MAX - 1))
338  {
340  return true;
341  }
342  needed = nprinted + 1;
343  }
344  else
345  {
346  /*
347  * We have to guess at how much to enlarge, since we're skipping the
348  * formatting work. Fortunately, because of enlargePQExpBuffer's
349  * preference for power-of-2 sizes, this number isn't very sensitive;
350  * the net effect is that we'll double the buffer size before trying
351  * to run vsnprintf, which seems sensible.
352  */
353  needed = 32;
354  }
355 
356  /* Increase the buffer size and try again. */
357  if (!enlargePQExpBuffer(str, needed))
358  return true; /* oops, out of memory */
359 
360  return false;
361 }
362 
363 /*
364  * appendPQExpBufferStr
365  * Append the given string to a PQExpBuffer, allocating more space
366  * if necessary.
367  */
368 void
370 {
371  appendBinaryPQExpBuffer(str, data, strlen(data));
372 }
373 
374 /*
375  * appendPQExpBufferChar
376  * Append a single byte to str.
377  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
378  */
379 void
381 {
382  /* Make more room if needed */
383  if (!enlargePQExpBuffer(str, 1))
384  return;
385 
386  /* OK, append the character */
387  str->data[str->len] = ch;
388  str->len++;
389  str->data[str->len] = '\0';
390 }
391 
392 /*
393  * appendBinaryPQExpBuffer
394  *
395  * Append arbitrary binary data to a PQExpBuffer, allocating more space
396  * if necessary.
397  */
398 void
399 appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
400 {
401  /* Make more room if needed */
402  if (!enlargePQExpBuffer(str, datalen))
403  return;
404 
405  /* OK, append the data */
406  memcpy(str->data + str->len, data, datalen);
407  str->len += datalen;
408 
409  /*
410  * Keep a trailing null in place, even though it's probably useless for
411  * binary data...
412  */
413  str->data[str->len] = '\0';
414 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
static bool static void markPQExpBufferBroken(PQExpBuffer str)
Definition: pqexpbuffer.c:52
PQExpBufferData * PQExpBuffer
Definition: pqexpbuffer.h:51
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
#define pg_attribute_printf(f, a)
Definition: c.h:132
#define malloc(a)
Definition: header.h:50
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:116
#define vsnprintf
Definition: port.h:191
static const char oom_buffer[1]
Definition: pqexpbuffer.c:38
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
#define unconstify(underlying_type, expr)
Definition: c.h:1194
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:74
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:380
#define free(a)
Definition: header.h:65
#define PQExpBufferBroken(str)
Definition: pqexpbuffer.h:59
#define realloc(a, b)
Definition: header.h:60
static const char * oom_buffer_ptr
Definition: pqexpbuffer.c:41
#define unlikely(x)
Definition: c.h:208
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:148
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
Definition: pqexpbuffer.c:399
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2
Definition: pqexpbuffer.c:296
#define INITIAL_EXPBUFFER_SIZE
Definition: pqexpbuffer.h:76
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:174