PostgreSQL Source Code  git master
psprintf.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * psprintf.c
4  * sprintf into an allocated-on-demand buffer
5  *
6  *
7  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/common/psprintf.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 
19 #include "postgres.h"
20 
21 #include "utils/memutils.h"
22 
23 #else
24 
25 #include "postgres_fe.h"
26 
27 /* It's possible we could use a different value for this in frontend code */
28 #define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
29 
30 #endif
31 
32 
33 /*
34  * psprintf
35  *
36  * Format text data under the control of fmt (an sprintf-style format string)
37  * and return it in an allocated-on-demand buffer. The buffer is allocated
38  * with palloc in the backend, or malloc in frontend builds. Caller is
39  * responsible to free the buffer when no longer needed, if appropriate.
40  *
41  * Errors are not returned to the caller, but are reported via elog(ERROR)
42  * in the backend, or printf-to-stderr-and-exit() in frontend builds.
43  * One should therefore think twice about using this in libpq.
44  */
45 char *
46 psprintf(const char *fmt,...)
47 {
48  int save_errno = errno;
49  size_t len = 128; /* initial assumption about buffer size */
50 
51  for (;;)
52  {
53  char *result;
54  va_list args;
55  size_t newlen;
56 
57  /*
58  * Allocate result buffer. Note that in frontend this maps to malloc
59  * with exit-on-error.
60  */
61  result = (char *) palloc(len);
62 
63  /* Try to format the data. */
64  errno = save_errno;
65  va_start(args, fmt);
66  newlen = pvsnprintf(result, len, fmt, args);
67  va_end(args);
68 
69  if (newlen < len)
70  return result; /* success */
71 
72  /* Release buffer and loop around to try again with larger len. */
73  pfree(result);
74  len = newlen;
75  }
76 }
77 
78 /*
79  * pvsnprintf
80  *
81  * Attempt to format text data under the control of fmt (an sprintf-style
82  * format string) and insert it into buf (which has length len).
83  *
84  * If successful, return the number of bytes emitted, not counting the
85  * trailing zero byte. This will always be strictly less than len.
86  *
87  * If there's not enough space in buf, return an estimate of the buffer size
88  * needed to succeed (this *must* be more than the given len, else callers
89  * might loop infinitely).
90  *
91  * Other error cases do not return, but exit via elog(ERROR) or exit().
92  * Hence, this shouldn't be used inside libpq.
93  *
94  * Caution: callers must be sure to preserve their entry-time errno
95  * when looping, in case the fmt contains "%m".
96  *
97  * Note that the semantics of the return value are not exactly C99's.
98  * First, we don't promise that the estimated buffer size is exactly right;
99  * callers must be prepared to loop multiple times to get the right size.
100  * (Given a C99-compliant vsnprintf, that won't happen, but it is rumored
101  * that some implementations don't always return the same value ...)
102  * Second, we return the recommended buffer size, not one less than that;
103  * this lets overflow concerns be handled here rather than in the callers.
104  */
105 size_t
106 pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
107 {
108  int nprinted;
109 
110  nprinted = vsnprintf(buf, len, fmt, args);
111 
112  /* We assume failure means the fmt is bogus, hence hard failure is OK */
113  if (unlikely(nprinted < 0))
114  {
115 #ifndef FRONTEND
116  elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
117 #else
118  fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n",
119  fmt);
121 #endif
122  }
123 
124  if ((size_t) nprinted < len)
125  {
126  /* Success. Note nprinted does not include trailing null. */
127  return (size_t) nprinted;
128  }
129 
130  /*
131  * We assume a C99-compliant vsnprintf, so believe its estimate of the
132  * required space, and add one for the trailing null. (If it's wrong, the
133  * logic will still work, but we may loop multiple times.)
134  *
135  * Choke if the required space would exceed MaxAllocSize. Note we use
136  * this palloc-oriented overflow limit even when in frontend.
137  */
138  if (unlikely((size_t) nprinted > MaxAllocSize - 1))
139  {
140 #ifndef FRONTEND
141  ereport(ERROR,
142  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
143  errmsg("out of memory")));
144 #else
145  fprintf(stderr, _("out of memory\n"));
147 #endif
148  }
149 
150  return nprinted + 1;
151 }
#define unlikely(x)
Definition: c.h:298
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
static void const char * fmt
va_end(args)
exit(1)
va_start(args, fmt)
void pfree(void *pointer)
Definition: mcxt.c:1508
void * palloc(Size size)
Definition: mcxt.c:1304
#define MaxAllocSize
Definition: memutils.h:40
const void size_t len
static char * buf
Definition: pg_test_fsync.c:73
#define vsnprintf
Definition: port.h:237
#define fprintf
Definition: port.h:242
size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
Definition: psprintf.c:106
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define EXIT_FAILURE
Definition: settings.h:167