diff options
author | Pavan Deolasee | 2015-05-05 09:19:18 +0000 |
---|---|---|
committer | Pavan Deolasee | 2015-05-05 09:19:18 +0000 |
commit | 73fa25c67cbfa24c03e28c96bf356f2592671730 (patch) | |
tree | 10ded7e26abd78d93658cb72fc5cb9d4672eff2a /src/common/psprintf.c | |
parent | da4d108859bcd7a308ca75aba54281e32968822c (diff) | |
parent | 4a9ab6d8619817f9e3989c99b65140e19041dab7 (diff) |
Merge branch 'XL_MASTER_MERGE_9_4' into XL_NEW_MASTER
Conflicts:
src/test/regress/expected/aggregates.out
src/test/regress/expected/create_index.out
src/test/regress/expected/inherit.out
src/test/regress/expected/join.out
src/test/regress/expected/window.out
src/test/regress/expected/with.out
Diffstat (limited to 'src/common/psprintf.c')
-rw-r--r-- | src/common/psprintf.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/src/common/psprintf.c b/src/common/psprintf.c new file mode 100644 index 0000000000..e1320a6db0 --- /dev/null +++ b/src/common/psprintf.c @@ -0,0 +1,190 @@ +/*------------------------------------------------------------------------- + * + * psprintf.c + * sprintf into an allocated-on-demand buffer + * + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/psprintf.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND + +#include "postgres.h" + +#include "utils/memutils.h" + +#else + +#include "postgres_fe.h" + +/* It's possible we could use a different value for this in frontend code */ +#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */ + +#endif + + +/* + * psprintf + * + * Format text data under the control of fmt (an sprintf-style format string) + * and return it in an allocated-on-demand buffer. The buffer is allocated + * with palloc in the backend, or malloc in frontend builds. Caller is + * responsible to free the buffer when no longer needed, if appropriate. + * + * Errors are not returned to the caller, but are reported via elog(ERROR) + * in the backend, or printf-to-stderr-and-exit() in frontend builds. + * One should therefore think twice about using this in libpq. + */ +char * +psprintf(const char *fmt,...) +{ + size_t len = 128; /* initial assumption about buffer size */ + + for (;;) + { + char *result; + va_list args; + size_t newlen; + + /* + * Allocate result buffer. Note that in frontend this maps to malloc + * with exit-on-error. + */ + result = (char *) palloc(len); + + /* Try to format the data. */ + va_start(args, fmt); + newlen = pvsnprintf(result, len, fmt, args); + va_end(args); + + if (newlen < len) + return result; /* success */ + + /* Release buffer and loop around to try again with larger len. */ + pfree(result); + len = newlen; + } +} + +/* + * pvsnprintf + * + * Attempt to format text data under the control of fmt (an sprintf-style + * format string) and insert it into buf (which has length len, len > 0). + * + * If successful, return the number of bytes emitted, not counting the + * trailing zero byte. This will always be strictly less than len. + * + * If there's not enough space in buf, return an estimate of the buffer size + * needed to succeed (this *must* be more than the given len, else callers + * might loop infinitely). + * + * Other error cases do not return, but exit via elog(ERROR) or exit(). + * Hence, this shouldn't be used inside libpq. + * + * This function exists mainly to centralize our workarounds for + * non-C99-compliant vsnprintf implementations. Generally, any call that + * pays any attention to the return value should go through here rather + * than calling snprintf or vsnprintf directly. + * + * Note that the semantics of the return value are not exactly C99's. + * First, we don't promise that the estimated buffer size is exactly right; + * callers must be prepared to loop multiple times to get the right size. + * Second, we return the recommended buffer size, not one less than that; + * this lets overflow concerns be handled here rather than in the callers. + */ +size_t +pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) +{ + int nprinted; + + Assert(len > 0); + + errno = 0; + + /* + * Assert check here is to catch buggy vsnprintf that overruns the + * specified buffer length. Solaris 7 in 64-bit mode is an example of a + * platform with such a bug. + */ +#ifdef USE_ASSERT_CHECKING + buf[len - 1] = '\0'; +#endif + + nprinted = vsnprintf(buf, len, fmt, args); + + Assert(buf[len - 1] == '\0'); + + /* + * If vsnprintf reports an error other than ENOMEM, fail. The possible + * causes of this are not user-facing errors, so elog should be enough. + */ + if (nprinted < 0 && errno != 0 && errno != ENOMEM) + { +#ifndef FRONTEND + elog(ERROR, "vsnprintf failed: %m"); +#else + fprintf(stderr, "vsnprintf failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); +#endif + } + + /* + * Note: some versions of vsnprintf return the number of chars actually + * stored, not the total space needed as C99 specifies. And at least one + * returns -1 on failure. Be conservative about believing whether the + * print worked. + */ + if (nprinted >= 0 && (size_t) nprinted < len - 1) + { + /* Success. Note nprinted does not include trailing null. */ + return (size_t) nprinted; + } + + if (nprinted >= 0 && (size_t) nprinted > len) + { + /* + * This appears to be a C99-compliant vsnprintf, so believe its + * estimate of the required space. (If it's wrong, the logic will + * still work, but we may loop multiple times.) Note that the space + * needed should be only nprinted+1 bytes, but we'd better allocate + * one more than that so that the test above will succeed next time. + * + * In the corner case where the required space just barely overflows, + * fall through so that we'll error out below (possibly after + * looping). + */ + if ((size_t) nprinted <= MaxAllocSize - 2) + return nprinted + 2; + } + + /* + * Buffer overrun, and we don't know how much space is needed. Estimate + * twice the previous buffer size, but not more than MaxAllocSize; if we + * are already at MaxAllocSize, choke. Note we use this palloc-oriented + * overflow limit even when in frontend. + */ + if (len >= MaxAllocSize) + { +#ifndef FRONTEND + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("out of memory"))); +#else + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); +#endif + } + + if (len >= MaxAllocSize / 2) + return MaxAllocSize; + + return len * 2; +} |