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