diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2018-09-26 13:31:56 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2018-09-26 13:31:56 -0400 |
commit | d6c55de1f99a9028540516316b95321a7b12a540 (patch) | |
tree | 7c95ff050f56bcf20648e3d7a8a7fbb2ddbc7293 /src/backend | |
parent | 96bf88d52711ad3a0a4cc2d1d9cb0e2acab85e63 (diff) | |
download | postgresql-d6c55de1f99a9028540516316b95321a7b12a540.tar.gz postgresql-d6c55de1f99a9028540516316b95321a7b12a540.zip |
Implement %m in src/port/snprintf.c, and teach elog.c to rely on that.
I started out with the idea that we needed to detect use of %m format specs
in contexts other than elog/ereport calls, because we couldn't rely on that
working in *printf calls. But a better answer is to fix things so that it
does work. Now that we're using snprintf.c all the time, we can implement
%m in that and we've fixed the problem.
This requires also adjusting our various printf-wrapping functions so that
they ensure "errno" is preserved when they call snprintf.c.
Remove elog.c's handmade implementation of %m, and let it rely on
snprintf to support the feature. That should provide some performance
gain, though I've not attempted to measure it.
There are a lot of places where we could now simplify 'printf("%s",
strerror(errno))' into 'printf("%m")', but I'm not in any big hurry
to make that happen.
Patch by me, reviewed by Michael Paquier
Discussion: https://postgr.es/m/2975.1526862605@sss.pgh.pa.us
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/lib/stringinfo.c | 6 | ||||
-rw-r--r-- | src/backend/utils/error/elog.c | 70 |
2 files changed, 10 insertions, 66 deletions
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c index 798a823ac9f..df7e01f76d9 100644 --- a/src/backend/lib/stringinfo.c +++ b/src/backend/lib/stringinfo.c @@ -77,12 +77,15 @@ resetStringInfo(StringInfo str) void appendStringInfo(StringInfo str, const char *fmt,...) { + int save_errno = errno; + for (;;) { va_list args; int needed; /* Try to format the data. */ + errno = save_errno; va_start(args, fmt); needed = appendStringInfoVA(str, fmt, args); va_end(args); @@ -105,6 +108,9 @@ appendStringInfo(StringInfo str, const char *fmt,...) * pass the return value to enlargeStringInfo() before trying again; see * appendStringInfo for standard usage pattern. * + * Caution: callers must be sure to preserve their entry-time errno + * when looping, in case the fmt contains "%m". + * * XXX This API is ugly, but there seems no alternative given the C spec's * restrictions on what can portably be done with va_list arguments: you have * to redo va_start before you can rescan the argument list, and we can't do diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 22e5d876181..b9c11301ca2 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -177,7 +177,6 @@ static void write_csvlog(ErrorData *edata); static void send_message_to_server_log(ErrorData *edata); static void write_pipe_chunks(char *data, int len, int dest); static void send_message_to_frontend(ErrorData *edata); -static char *expand_fmt_string(const char *fmt, ErrorData *edata); static const char *error_severity(int elevel); static void append_with_tabs(StringInfo buf, const char *str); static bool is_log_level_output(int elevel, int log_min_level); @@ -705,13 +704,10 @@ errcode_for_socket_access(void) */ #define EVALUATE_MESSAGE(domain, targetfield, appendval, translateit) \ { \ - char *fmtbuf; \ StringInfoData buf; \ /* Internationalize the error format string */ \ if ((translateit) && !in_error_recursion_trouble()) \ fmt = dgettext((domain), fmt); \ - /* Expand %m in format string */ \ - fmtbuf = expand_fmt_string(fmt, edata); \ initStringInfo(&buf); \ if ((appendval) && edata->targetfield) { \ appendStringInfoString(&buf, edata->targetfield); \ @@ -722,15 +718,14 @@ errcode_for_socket_access(void) { \ va_list args; \ int needed; \ + errno = edata->saved_errno; \ va_start(args, fmt); \ - needed = appendStringInfoVA(&buf, fmtbuf, args); \ + needed = appendStringInfoVA(&buf, fmt, args); \ va_end(args); \ if (needed == 0) \ break; \ enlargeStringInfo(&buf, needed); \ } \ - /* Done with expanded fmt */ \ - pfree(fmtbuf); \ /* Save the completed message into the stack item */ \ if (edata->targetfield) \ pfree(edata->targetfield); \ @@ -746,15 +741,12 @@ errcode_for_socket_access(void) #define EVALUATE_MESSAGE_PLURAL(domain, targetfield, appendval) \ { \ const char *fmt; \ - char *fmtbuf; \ StringInfoData buf; \ /* Internationalize the error format string */ \ if (!in_error_recursion_trouble()) \ fmt = dngettext((domain), fmt_singular, fmt_plural, n); \ else \ fmt = (n == 1 ? fmt_singular : fmt_plural); \ - /* Expand %m in format string */ \ - fmtbuf = expand_fmt_string(fmt, edata); \ initStringInfo(&buf); \ if ((appendval) && edata->targetfield) { \ appendStringInfoString(&buf, edata->targetfield); \ @@ -765,15 +757,14 @@ errcode_for_socket_access(void) { \ va_list args; \ int needed; \ + errno = edata->saved_errno; \ va_start(args, n); \ - needed = appendStringInfoVA(&buf, fmtbuf, args); \ + needed = appendStringInfoVA(&buf, fmt, args); \ va_end(args); \ if (needed == 0) \ break; \ enlargeStringInfo(&buf, needed); \ } \ - /* Done with expanded fmt */ \ - pfree(fmtbuf); \ /* Save the completed message into the stack item */ \ if (edata->targetfield) \ pfree(edata->targetfield); \ @@ -3329,59 +3320,6 @@ send_message_to_frontend(ErrorData *edata) /* - * expand_fmt_string --- process special format codes in a format string - * - * We must replace %m with the appropriate strerror string, since vsnprintf - * won't know what to do with it. - * - * The result is a palloc'd string. - */ -static char * -expand_fmt_string(const char *fmt, ErrorData *edata) -{ - StringInfoData buf; - const char *cp; - - initStringInfo(&buf); - - for (cp = fmt; *cp; cp++) - { - if (cp[0] == '%' && cp[1] != '\0') - { - cp++; - if (*cp == 'm') - { - /* - * Replace %m by system error string. If there are any %'s in - * the string, we'd better double them so that vsnprintf won't - * misinterpret. - */ - const char *cp2; - - cp2 = strerror(edata->saved_errno); - for (; *cp2; cp2++) - { - if (*cp2 == '%') - appendStringInfoCharMacro(&buf, '%'); - appendStringInfoCharMacro(&buf, *cp2); - } - } - else - { - /* copy % and next char --- this avoids trouble with %%m */ - appendStringInfoCharMacro(&buf, '%'); - appendStringInfoCharMacro(&buf, *cp); - } - } - else - appendStringInfoCharMacro(&buf, *cp); - } - - return buf.data; -} - - -/* * error_severity --- get string representing elevel * * The string is not localized here, but we mark the strings for translation |