Coverage Report

Created: 2025-06-13 06:06

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