Обсуждение: Allowing printf("%m") only where it actually works

Поиск
Список
Период
Сортировка

Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
<digression>

For amusement's sake, I was playing around with NetBSD-current (9-to-be)
today, and tried to compile Postgres on it.  It works OK --- and I can
even confirm that our new code for using ARM v8 CRC instructions works
there --- but I got a boatload of compile warnings like this:

latch.c:1180:4: warning: %m is only allowed in syslog(3) like functions [-Wformat=]
    ereport(ERROR,
    ^~~~~~~

A bit of googling turned up the patch that caused this [1], which was
soon followed by some well-reasoned push-back [2]; but the warning's
still there, so evidently the forces of bullheadedness won.  I was
ready to discount the whole thing as being another badly designed
no-wonder-gcc-upstream-won't-take-it compiler warning, when I noticed that
the last few warnings in my output were pointing out a live bug, to wit
using %m with plain old printf rather than elog/ereport.  So I fixed
that [3], but I'm thinking that we need to take a bit more care here.

</digression>

Looking around, we have circa seventy-five functions declared with
pg_attribute_printf in our tree right now, and of those, *only* the
elog/ereport support functions can be relied on to process %m correctly.
However, anyone who's accustomed to working in backend code is likely to
not think hard about using %m in an error message, as indeed the authors
and reviewers of pg_verify_checksums did not.  Worse yet, such cases
actually will work as long as you're testing on glibc platforms, only
to fail everywhere else.

So I think we need to try to make provisions for getting compiler warnings
when %m is used in a function that doesn't support it.  gcc on Linux-ish
platforms isn't going to be very helpful with this, but that doesn't mean
that we should confuse %m-supporting and not-%m-supporting functions,
as we do right now.

Hence, I think we need something roughly like the attached patch, which
arranges to use "gnu_printf" (if available) as the format archetype for
the elog/ereport functions, and plain "printf" for all the rest.  With
some additional hackery not included here, this can be ju-jitsu'd into
compiling warning-free on NetBSD-current.  (The basic idea is to extend
PGAC_C_PRINTF_ARCHETYPE so it will select __syslog__ as the archetype
if available; but then you need some hack to suppress the follow-on
warnings complained of in [2].  I haven't decided what's the least ugly
solution for the latter, so I'm not proposing such a patch yet.)

What I'm mainly concerned about at this stage is what effects this'll
have on Windows builds.  The existing comment for PGAC_C_PRINTF_ARCHETYPE
claims that using gnu_printf silences complaints about "%lld" and related
formats on Windows, but I wonder whether that is still true on Windows
versions we still support.  As I mentioned in [4], I don't think we really
work any longer on platforms that don't use "%lld" for "long long" values,
and it seems that Windows does accept that in post-XP versions --- but has
gcc gotten the word?

If this does work as desired on Windows, then that would be a fairly
mainstream platform that can produce warnings about wrong uses of %m,
even if gcc-on-Linux doesn't.  If worst comes to worst, somebody could
crank up a buildfarm machine with a newer NetBSD release.

Anyway, I don't feel a need to cram this into v11, since I just fixed
the live bugs of this ilk in HEAD; it seems like it can wait for v12.
So I'll add this to the next commitfest.

            regards, tom lane

[1] https://mail-index.netbsd.org/tech-userlevel/2015/08/21/msg009282.html
[2] https://mail-index.netbsd.org/tech-userlevel/2015/10/23/msg009371.html
[3] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=a13b47a59ffce6f3c13c8b777738a3aab1db10d3
[4] https://www.postgresql.org/message-id/13103.1526749980@sss.pgh.pa.us

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index ba5c40d..5cd8994 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -19,10 +19,10 @@ fi])# PGAC_C_SIGNED

 # PGAC_C_PRINTF_ARCHETYPE
 # -----------------------
-# Set the format archetype used by gcc to check printf type functions.  We
-# prefer "gnu_printf", which includes what glibc uses, such as %m for error
-# strings and %lld for 64 bit long longs.  GCC 4.4 introduced it.  It makes a
-# dramatic difference on Windows.
+# Set the format archetype used by gcc to check elog/ereport functions.
+# This should accept %m, whether or not the platform's printf does.
+# We use "gnu_printf" if possible, which does that, although in some cases
+# it might do more than we could wish.
 AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
 [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
 [ac_save_c_werror_flag=$ac_c_werror_flag
@@ -34,8 +34,8 @@ __attribute__((format(gnu_printf, 2, 3)));], [])],
                   [pgac_cv_printf_archetype=gnu_printf],
                   [pgac_cv_printf_archetype=printf])
 ac_c_werror_flag=$ac_save_c_werror_flag])
-AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
-                   [Define to gnu_printf if compiler supports it, else printf.])
+AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE_M], [$pgac_cv_printf_archetype],
+                   [Define as a format archetype that accepts %m, if available, else printf.])
 ])# PGAC_PRINTF_ARCHETYPE


diff --git a/configure b/configure
index b244fc3..df12bb5 100755
--- a/configure
+++ b/configure
@@ -13305,7 +13305,7 @@ fi
 $as_echo "$pgac_cv_printf_archetype" >&6; }

 cat >>confdefs.h <<_ACEOF
-#define PG_PRINTF_ATTRIBUTE $pgac_cv_printf_archetype
+#define PG_PRINTF_ATTRIBUTE_M $pgac_cv_printf_archetype
 _ACEOF


diff --git a/src/include/c.h b/src/include/c.h
index 1e50103..0a4757e 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -126,10 +126,14 @@
 /* GCC and XLC support format attributes */
 #if defined(__GNUC__) || defined(__IBMC__)
 #define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
-#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a)))
+/* Use for functions wrapping stdio's printf, which often doesn't take %m: */
+#define pg_attribute_printf(f,a) __attribute__((format(printf, f, a)))
+/* Use for elog/ereport, which implement %m for themselves: */
+#define pg_attribute_printf_m(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE_M, f, a)))
 #else
 #define pg_attribute_format_arg(a)
 #define pg_attribute_printf(f,a)
+#define pg_attribute_printf_m(f,a)
 #endif

 /* GCC, Sunpro and XLC support aligned, packed and noreturn */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c3320f2..92daabc 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -799,8 +799,8 @@
 /* PostgreSQL major version as a string */
 #undef PG_MAJORVERSION

-/* Define to gnu_printf if compiler supports it, else printf. */
-#undef PG_PRINTF_ATTRIBUTE
+/* Define as a format archetype that accepts %m, if available, else printf. */
+#undef PG_PRINTF_ATTRIBUTE_M

 /* PostgreSQL version as a string */
 #undef PG_VERSION
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7a9ba7f..4f4091d 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -133,25 +133,25 @@ extern int    errcode(int sqlerrcode);
 extern int    errcode_for_file_access(void);
 extern int    errcode_for_socket_access(void);

-extern int    errmsg(const char *fmt,...) pg_attribute_printf(1, 2);
-extern int    errmsg_internal(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int    errmsg(const char *fmt,...) pg_attribute_printf_m(1, 2);
+extern int    errmsg_internal(const char *fmt,...) pg_attribute_printf_m(1, 2);

 extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural,
-              unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);
+              unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

-extern int    errdetail(const char *fmt,...) pg_attribute_printf(1, 2);
-extern int    errdetail_internal(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int    errdetail(const char *fmt,...) pg_attribute_printf_m(1, 2);
+extern int    errdetail_internal(const char *fmt,...) pg_attribute_printf_m(1, 2);

-extern int    errdetail_log(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int    errdetail_log(const char *fmt,...) pg_attribute_printf_m(1, 2);

 extern int errdetail_log_plural(const char *fmt_singular,
                      const char *fmt_plural,
-                     unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);
+                     unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

 extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural,
-                 unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);
+                 unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

-extern int    errhint(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int    errhint(const char *fmt,...) pg_attribute_printf_m(1, 2);

 /*
  * errcontext() is typically called in error context callback functions, not
@@ -165,7 +165,7 @@ extern int    errhint(const char *fmt,...) pg_attribute_printf(1, 2);

 extern int    set_errcontext_domain(const char *domain);

-extern int    errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int    errcontext_msg(const char *fmt,...) pg_attribute_printf_m(1, 2);

 extern int    errhidestmt(bool hide_stmt);
 extern int    errhidecontext(bool hide_ctx);
@@ -222,13 +222,13 @@ extern int    getinternalerrposition(void);
 #endif                            /* HAVE__VA_ARGS */

 extern void elog_start(const char *filename, int lineno, const char *funcname);
-extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf(2, 3);
+extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf_m(2, 3);


 /* Support for constructing error strings separately from ereport() calls */

 extern void pre_format_elog_string(int errnumber, const char *domain);
-extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2);
+extern char *format_elog_string(const char *fmt,...) pg_attribute_printf_m(1, 2);


 /* Support for attaching context information to error reports */
@@ -407,9 +407,9 @@ extern void set_syslog_parameters(const char *ident, int facility);
 #endif

 /*
- * Write errors to stderr (or by equal means when stderr is
- * not available). Used before ereport/elog can be used
- * safely (memory context, GUC load etc)
+ * Write errors to stderr (or by comparable means when stderr is not
+ * available).  Used before ereport/elog can be used safely (memory context,
+ * GUC load etc).  Note that this does *not* accept "%m".
  */
 extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);


Re: Allowing printf("%m") only where it actually works

От
Thomas Munro
Дата:
On Mon, May 21, 2018 at 12:30 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> For amusement's sake, I was playing around with NetBSD-current (9-to-be)
> today, and tried to compile Postgres on it.  It works OK --- and I can
> even confirm that our new code for using ARM v8 CRC instructions works

Excellent news.

> there --- but I got a boatload of compile warnings like this:
>
> latch.c:1180:4: warning: %m is only allowed in syslog(3) like functions [-Wformat=]
>     ereport(ERROR,
>     ^~~~~~~
>
> A bit of googling turned up the patch that caused this [1], which was
> soon followed by some well-reasoned push-back [2]; but the warning's
> still there, so evidently the forces of bullheadedness won.  I was
> ready to discount the whole thing as being another badly designed
> no-wonder-gcc-upstream-won't-take-it compiler warning, when I noticed that
> the last few warnings in my output were pointing out a live bug, to wit
> using %m with plain old printf rather than elog/ereport.  So I fixed
> that [3], but I'm thinking that we need to take a bit more care here.

I tried this on macOS and FreeBSD using GCC and Clang: both accept
printf("%m") without warning and then just print out "m".  It'll be
interesting to see if the NetBSD patch/idea travels further or some
other solution can be found.  I've raised this on the freebsd-hackers
list, let's see... I bet there's other software out there that just
prints out "m" when things go wrong.  It's arguably something that
you'd want the complier to understand as a C dialect thing.

-- 
Thomas Munro
http://www.enterprisedb.com


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Thomas Munro <thomas.munro@enterprisedb.com> writes:
> I tried this on macOS and FreeBSD using GCC and Clang: both accept
> printf("%m") without warning and then just print out "m".  It'll be
> interesting to see if the NetBSD patch/idea travels further or some
> other solution can be found.  I've raised this on the freebsd-hackers
> list, let's see... I bet there's other software out there that just
> prints out "m" when things go wrong.  It's arguably something that
> you'd want the complier to understand as a C dialect thing.

Yeah.  ISTM that the netbsd guys didn't get this quite right.  The
gcc docs are perfectly clear about what they think the semantics should
be:

    The parameter archetype determines how the format string is
    interpreted, and should be printf, scanf, strftime, gnu_printf,
    gnu_scanf, gnu_strftime or strfmon. ...  archetype values such as
    printf refer to the formats accepted by the system’s C runtime
    library, while values prefixed with ‘gnu_’ always refer to the formats
    accepted by the GNU C Library.

Therefore, what netbsd should have done was leave the semantics of
"gnu_printf" alone (because glibc undoubtedly does take %m), and just emit
a warning with the "printf" archetype --- which is supposed to describe
the behavior of the platform's stdio, which in their case is known not
to take %m.  If they'd done it that way then they'd have a patch that gcc
upstream certainly ought to accept, because it follows gcc's own stated
semantics for the check.  This would also partially resolve the complaint
Roy Marples had in the thread I alluded to, ie he could use "gnu_printf"
to describe a function that accepts %m.  (There might still need to be
some work to be done to avoid bogus -Wmissing-format-attribute complaints,
not sure.)  I'm not sure whether a separate archetype for syslog is really
needed.  Formally you could say that distinguishing syslog from GNU printf
is wise, but I'm not sure I see a practical need for it.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
... and, while we're thinking about this, how can we prevent the reverse
problem of using strerror(errno) where you should have used %m?  That
is evidently not academic either, cf 81256cd.

I am wondering whether the elog/ereport macros can locally define some
version of "errno" that would cause a compile failure if it's referenced
within the macro args.  But I'm too tired to work it out in detail.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Thomas Munro
Дата:
On Mon, May 21, 2018 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> ... and, while we're thinking about this, how can we prevent the reverse
> problem of using strerror(errno) where you should have used %m?  That
> is evidently not academic either, cf 81256cd.
>
> I am wondering whether the elog/ereport macros can locally define some
> version of "errno" that would cause a compile failure if it's referenced
> within the macro args.  But I'm too tired to work it out in detail.

Here's an experimental way to do that, if you don't mind depending on
gory details of libc implementations (ie knowledge of what it expands
too).  Not sure how to avoid that since it's a macro on all modern
systems, and we don't have a way to temporarily redefine a macro.  If
you enable it for just ereport(), it compiles cleanly after 81256cd
(but fails on earlier commits).  If you enable it for elog() too then
it finds problems with exec.c.

Another idea:  if there are any systems in the build farm where it
isn't a macro as permitted by the standard (#ifndef errno), you could
perhaps define it as something uncompilable and then undefined it at
the end of the scope, so we'd at least have a post-commit canary.

-- 
Thomas Munro
http://www.enterprisedb.com

Вложения

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Thomas Munro <thomas.munro@enterprisedb.com> writes:
> On Mon, May 21, 2018 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> I am wondering whether the elog/ereport macros can locally define some
>> version of "errno" that would cause a compile failure if it's referenced
>> within the macro args.  But I'm too tired to work it out in detail.

> Here's an experimental way to do that, if you don't mind depending on
> gory details of libc implementations (ie knowledge of what it expands
> too).  Not sure how to avoid that since it's a macro on all modern
> systems, and we don't have a way to temporarily redefine a macro.  If
> you enable it for just ereport(), it compiles cleanly after 81256cd
> (but fails on earlier commits).  If you enable it for elog() too then
> it finds problems with exec.c.

Hmm ... that's pretty duff code in exec.c, isn't it.  Aside from the
question of errno unsafety, it's using elog where it really ought to be
using ereport, it's not taking any thought for the reported SQLSTATE,
etc.  I'm hesitant to mess with it mere hours before the beta wrap,
but we really oughta improve that.

I noticed another can of worms here, too: on Windows, doesn't use of
GetLastError() in elog/ereport have exactly the same hazard as errno?
Or is there some reason to think it can't change value during errstart()?

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Thomas Munro
Дата:
On Mon, May 21, 2018 at 2:41 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:
> I've raised this on the freebsd-hackers
> list, let's see... I bet there's other software out there that just
> prints out "m" when things go wrong.  It's arguably something that
> you'd want the complier to understand as a C dialect thing.

Result: FreeBSD printf() now supports %m, and an issue is being raised
with clang about lack of warnings on OSes that don't grok %m, and lack
of warnings when you say -Wformat-non-iso on any OS.  Let's see how
that goes.

-- 
Thomas Munro
http://www.enterprisedb.com


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> Thomas Munro <thomas.munro@enterprisedb.com> writes:
>> Here's an experimental way to do that, if you don't mind depending on
>> gory details of libc implementations (ie knowledge of what it expands
>> too).  Not sure how to avoid that since it's a macro on all modern
>> systems, and we don't have a way to temporarily redefine a macro.  If
>> you enable it for just ereport(), it compiles cleanly after 81256cd
>> (but fails on earlier commits).  If you enable it for elog() too then
>> it finds problems with exec.c.

> Hmm ... that's pretty duff code in exec.c, isn't it.  Aside from the
> question of errno unsafety, it's using elog where it really ought to be
> using ereport, it's not taking any thought for the reported SQLSTATE,
> etc.  I'm hesitant to mess with it mere hours before the beta wrap,
> but we really oughta improve that.

I wrote up a patch that makes src/common/exec.c do error reporting more
like other frontend/backend-common files (attached).  Now that I've done
so, though, I'm having second thoughts.  The thing that I don't like
about this is that it doubles the number of translatable strings created
by this file.  While there's not *that* many of them, translators have to
deal with each one several times because this file is included by several
different frontend programs.  So that seems like a rather high price to
pay to deal with what, at present, is a purely hypothetical hazard.
(Basically what this would protect against is elog_start changing errno,
which it doesn't.)  Improving the errcode situation is somewhat useful,
but still maybe it's not worth the cost.

Another approach we could consider is keeping exec.c's one-off approach
to error handling and letting it redefine pg_prevent_errno_in_scope() as
empty.  But that's ugly.

Or we could make the affected call sites work like this:

        int save_errno = errno;

        log_error(_("could not identify current directory: %s"),
                  strerror(save_errno));

which on the whole might be the most expedient thing.

            regards, tom lane

diff --git a/src/common/exec.c b/src/common/exec.c
index 4df16cd..f0d52e1 100644
*** a/src/common/exec.c
--- b/src/common/exec.c
***************
*** 25,40 ****
  #include <sys/wait.h>
  #include <unistd.h>

- #ifndef FRONTEND
- /* We use only 3- and 4-parameter elog calls in this file, for simplicity */
- /* NOTE: caller must provide gettext call around str! */
- #define log_error(str, param)    elog(LOG, str, param)
- #define log_error4(str, param, arg1)    elog(LOG, str, param, arg1)
- #else
- #define log_error(str, param)    (fprintf(stderr, str, param), fputc('\n', stderr))
- #define log_error4(str, param, arg1)    (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
- #endif
-
  #ifdef _MSC_VER
  #define getcwd(cwd,len)  GetCurrentDirectory(len, cwd)
  #endif
--- 25,30 ----
*************** find_my_exec(const char *argv0, char *re
*** 124,131 ****

      if (!getcwd(cwd, MAXPGPATH))
      {
!         log_error(_("could not identify current directory: %s"),
!                   strerror(errno));
          return -1;
      }

--- 114,127 ----

      if (!getcwd(cwd, MAXPGPATH))
      {
! #ifndef FRONTEND
!         ereport(LOG,
!                 (errcode_for_file_access(),
!                  errmsg("could not identify current directory: %m")));
! #else
!         fprintf(stderr, _("could not identify current directory: %s\n"),
!                 strerror(errno));
! #endif
          return -1;
      }

*************** find_my_exec(const char *argv0, char *re
*** 143,149 ****
          if (validate_exec(retpath) == 0)
              return resolve_symlinks(retpath);

!         log_error(_("invalid binary \"%s\""), retpath);
          return -1;
      }

--- 139,149 ----
          if (validate_exec(retpath) == 0)
              return resolve_symlinks(retpath);

! #ifndef FRONTEND
!         ereport(LOG, (errmsg("invalid binary \"%s\"", retpath)));
! #else
!         fprintf(stderr, _("invalid binary \"%s\"\n"), retpath);
! #endif
          return -1;
      }

*************** find_my_exec(const char *argv0, char *re
*** 192,205 ****
                  case -1:        /* wasn't even a candidate, keep looking */
                      break;
                  case -2:        /* found but disqualified */
!                     log_error(_("could not read binary \"%s\""),
!                               retpath);
                      break;
              }
          } while (*endp);
      }

!     log_error(_("could not find a \"%s\" to execute"), argv0);
      return -1;
  }

--- 192,214 ----
                  case -1:        /* wasn't even a candidate, keep looking */
                      break;
                  case -2:        /* found but disqualified */
! #ifndef FRONTEND
!                     ereport(LOG, (errmsg("could not read binary \"%s\"",
!                                          retpath)));
! #else
!                     fprintf(stderr, _("could not read binary \"%s\"\n"),
!                             retpath);
! #endif
                      break;
              }
          } while (*endp);
      }

! #ifndef FRONTEND
!     ereport(LOG, (errmsg("could not find a \"%s\" to execute", argv0)));
! #else
!     fprintf(stderr, _("could not find a \"%s\" to execute\n"), argv0);
! #endif
      return -1;
  }

*************** resolve_symlinks(char *path)
*** 238,245 ****
       */
      if (!getcwd(orig_wd, MAXPGPATH))
      {
!         log_error(_("could not identify current directory: %s"),
!                   strerror(errno));
          return -1;
      }

--- 247,260 ----
       */
      if (!getcwd(orig_wd, MAXPGPATH))
      {
! #ifndef FRONTEND
!         ereport(LOG,
!                 (errcode_for_file_access(),
!                  errmsg("could not identify current directory: %m")));
! #else
!         fprintf(stderr, _("could not identify current directory: %s\n"),
!                 strerror(errno));
! #endif
          return -1;
      }

*************** resolve_symlinks(char *path)
*** 254,260 ****
              *lsep = '\0';
              if (chdir(path) == -1)
              {
!                 log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
                  return -1;
              }
              fname = lsep + 1;
--- 269,283 ----
              *lsep = '\0';
              if (chdir(path) == -1)
              {
! #ifndef FRONTEND
!                 ereport(LOG,
!                         (errcode_for_file_access(),
!                          errmsg("could not change directory to \"%s\": %m",
!                                 path)));
! #else
!                 fprintf(stderr, _("could not change directory to \"%s\": %s\n"),
!                         path, strerror(errno));
! #endif
                  return -1;
              }
              fname = lsep + 1;
*************** resolve_symlinks(char *path)
*** 269,275 ****
          rllen = readlink(fname, link_buf, sizeof(link_buf));
          if (rllen < 0 || rllen >= sizeof(link_buf))
          {
!             log_error(_("could not read symbolic link \"%s\""), fname);
              return -1;
          }
          link_buf[rllen] = '\0';
--- 292,303 ----
          rllen = readlink(fname, link_buf, sizeof(link_buf));
          if (rllen < 0 || rllen >= sizeof(link_buf))
          {
! #ifndef FRONTEND
!             ereport(LOG,
!                     (errmsg("could not read symbolic link \"%s\"", fname)));
! #else
!             fprintf(stderr, _("could not read symbolic link \"%s\"\n"), fname);
! #endif
              return -1;
          }
          link_buf[rllen] = '\0';
*************** resolve_symlinks(char *path)
*** 281,288 ****

      if (!getcwd(path, MAXPGPATH))
      {
!         log_error(_("could not identify current directory: %s"),
!                   strerror(errno));
          return -1;
      }
      join_path_components(path, path, link_buf);
--- 309,322 ----

      if (!getcwd(path, MAXPGPATH))
      {
! #ifndef FRONTEND
!         ereport(LOG,
!                 (errcode_for_file_access(),
!                  errmsg("could not identify current directory: %m")));
! #else
!         fprintf(stderr, _("could not identify current directory: %s\n"),
!                 strerror(errno));
! #endif
          return -1;
      }
      join_path_components(path, path, link_buf);
*************** resolve_symlinks(char *path)
*** 290,296 ****

      if (chdir(orig_wd) == -1)
      {
!         log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
          return -1;
      }
  #endif                            /* HAVE_READLINK */
--- 324,338 ----

      if (chdir(orig_wd) == -1)
      {
! #ifndef FRONTEND
!         ereport(LOG,
!                 (errcode_for_file_access(),
!                  errmsg("could not change directory to \"%s\": %m",
!                         orig_wd)));
! #else
!         fprintf(stderr, _("could not change directory to \"%s\": %s\n"),
!                 orig_wd, strerror(errno));
! #endif
          return -1;
      }
  #endif                            /* HAVE_READLINK */
*************** pclose_check(FILE *stream)
*** 520,536 ****
      if (exitstatus == -1)
      {
          /* pclose() itself failed, and hopefully set errno */
!         log_error(_("pclose failed: %s"), strerror(errno));
      }
      else
      {
          reason = wait_result_to_str(exitstatus);
!         log_error("%s", reason);
! #ifdef FRONTEND
!         free(reason);
  #else
!         pfree(reason);
  #endif
      }
      return exitstatus;
  }
--- 562,582 ----
      if (exitstatus == -1)
      {
          /* pclose() itself failed, and hopefully set errno */
! #ifndef FRONTEND
!         elog(LOG, "pclose failed: %m");
! #else
!         fprintf(stderr, "pclose failed: %s\n", strerror(errno));
! #endif
      }
      else
      {
          reason = wait_result_to_str(exitstatus);
! #ifndef FRONTEND
!         elog(LOG, "%s", reason);
  #else
!         fprintf(stderr, "%s\n", reason);
  #endif
+         pfree(reason);
      }
      return exitstatus;
  }
*************** AddUserToTokenDacl(HANDLE hToken)
*** 651,669 ****
              ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
              if (ptdd == NULL)
              {
!                 log_error("could not allocate %lu bytes of memory", dwSize);
                  goto cleanup;
              }

              if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
              {
!                 log_error("could not get token information: error code %lu", GetLastError());
                  goto cleanup;
              }
          }
          else
          {
!             log_error("could not get token information buffer size: error code %lu", GetLastError());
              goto cleanup;
          }
      }
--- 697,727 ----
              ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
              if (ptdd == NULL)
              {
! #ifndef FRONTEND
!                 elog(LOG, "out of memory");
! #else
!                 fprintf(stderr, "out of memory\n");
! #endif
                  goto cleanup;
              }

              if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
              {
! #ifndef FRONTEND
!                 elog(LOG, "could not get token information: error code %lu", GetLastError());
! #else
!                 fprintf(stderr, "could not get token information: error code %lu\n", GetLastError());
! #endif
                  goto cleanup;
              }
          }
          else
          {
! #ifndef FRONTEND
!             elog(LOG, "could not get token information buffer size: error code %lu", GetLastError());
! #else
!             fprintf(stderr, "could not get token information buffer size: error code %lu\n", GetLastError());
! #endif
              goto cleanup;
          }
      }
*************** AddUserToTokenDacl(HANDLE hToken)
*** 673,679 ****
                             (DWORD) sizeof(ACL_SIZE_INFORMATION),
                             AclSizeInformation))
      {
!         log_error("could not get ACL information: error code %lu", GetLastError());
          goto cleanup;
      }

--- 731,741 ----
                             (DWORD) sizeof(ACL_SIZE_INFORMATION),
                             AclSizeInformation))
      {
! #ifndef FRONTEND
!         elog(LOG, "could not get ACL information: error code %lu", GetLastError());
! #else
!         fprintf(stderr, "could not get ACL information: error code %lu\n", GetLastError());
! #endif
          goto cleanup;
      }

*************** AddUserToTokenDacl(HANDLE hToken)
*** 689,701 ****
      pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
      if (pacl == NULL)
      {
!         log_error("could not allocate %lu bytes of memory", dwNewAclSize);
          goto cleanup;
      }

      if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
      {
!         log_error("could not initialize ACL: error code %lu", GetLastError());
          goto cleanup;
      }

--- 751,771 ----
      pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
      if (pacl == NULL)
      {
! #ifndef FRONTEND
!         elog(LOG, "out of memory");
! #else
!         fprintf(stderr, "out of memory\n");
! #endif
          goto cleanup;
      }

      if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
      {
! #ifndef FRONTEND
!         elog(LOG, "could not initialize ACL: error code %lu", GetLastError());
! #else
!         fprintf(stderr, "could not initialize ACL: error code %lu\n", GetLastError());
! #endif
          goto cleanup;
      }

*************** AddUserToTokenDacl(HANDLE hToken)
*** 704,716 ****
      {
          if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
          {
!             log_error("could not get ACE: error code %lu", GetLastError());
              goto cleanup;
          }

          if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
          {
!             log_error("could not add ACE: error code %lu", GetLastError());
              goto cleanup;
          }
      }
--- 774,794 ----
      {
          if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
          {
! #ifndef FRONTEND
!             elog(LOG, "could not get ACE: error code %lu", GetLastError());
! #else
!             fprintf(stderr, "could not get ACE: error code %lu\n", GetLastError());
! #endif
              goto cleanup;
          }

          if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
          {
! #ifndef FRONTEND
!             elog(LOG, "could not add ACE: error code %lu", GetLastError());
! #else
!             fprintf(stderr, "could not add ACE: error code %lu\n", GetLastError());
! #endif
              goto cleanup;
          }
      }
*************** AddUserToTokenDacl(HANDLE hToken)
*** 718,724 ****
      /* Add the new ACE for the current user */
      if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
      {
!         log_error("could not add access allowed ACE: error code %lu", GetLastError());
          goto cleanup;
      }

--- 796,806 ----
      /* Add the new ACE for the current user */
      if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
      {
! #ifndef FRONTEND
!         elog(LOG, "could not add access allowed ACE: error code %lu", GetLastError());
! #else
!         fprintf(stderr, "could not add access allowed ACE: error code %lu\n", GetLastError());
! #endif
          goto cleanup;
      }

*************** AddUserToTokenDacl(HANDLE hToken)
*** 727,733 ****

      if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
      {
!         log_error("could not set token information: error code %lu", GetLastError());
          goto cleanup;
      }

--- 809,819 ----

      if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
      {
! #ifndef FRONTEND
!         elog(LOG, "could not set token information: error code %lu", GetLastError());
! #else
!         fprintf(stderr, "could not set token information: error code %lu\n", GetLastError());
! #endif
          goto cleanup;
      }

*************** GetTokenUser(HANDLE hToken, PTOKEN_USER
*** 773,785 ****

              if (*ppTokenUser == NULL)
              {
!                 log_error("could not allocate %lu bytes of memory", dwLength);
                  return FALSE;
              }
          }
          else
          {
!             log_error("could not get token information buffer size: error code %lu", GetLastError());
              return FALSE;
          }
      }
--- 859,879 ----

              if (*ppTokenUser == NULL)
              {
! #ifndef FRONTEND
!                 elog(LOG, "out of memory");
! #else
!                 fprintf(stderr, "out of memory\n");
! #endif
                  return FALSE;
              }
          }
          else
          {
! #ifndef FRONTEND
!             elog(LOG, "could not get token information buffer size: error code %lu", GetLastError());
! #else
!             fprintf(stderr, "could not get token information buffer size: error code %lu\n", GetLastError());
! #endif
              return FALSE;
          }
      }
*************** GetTokenUser(HANDLE hToken, PTOKEN_USER
*** 793,799 ****
          LocalFree(*ppTokenUser);
          *ppTokenUser = NULL;

!         log_error("could not get token information: error code %lu", GetLastError());
          return FALSE;
      }

--- 887,897 ----
          LocalFree(*ppTokenUser);
          *ppTokenUser = NULL;

! #ifndef FRONTEND
!         elog(LOG, "could not get token information: error code %lu", GetLastError());
! #else
!         fprintf(stderr, "could not get token information: error code %lu\n", GetLastError());
! #endif
          return FALSE;
      }


Re: Allowing printf("%m") only where it actually works

От
Thomas Munro
Дата:
On Sun, May 27, 2018 at 4:21 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> ...  So that seems like a rather high price to
> pay to deal with what, at present, is a purely hypothetical hazard.
> (Basically what this would protect against is elog_start changing errno,
> which it doesn't.)

Hmm.  It looks like errstart() preserves errno to protect %m not from
itself, but from the caller's other arguments to the elog facility.
That seems reasonable, but do we really need to prohibit direct use of
errno in expressions?  The only rogue actor likely to trash errno is
you, the caller.  I mean, if you call elog(LOG, "foo %d %d", errno,
fsync(bar)) it's obviously UB and your own fault, but who would do
anything like that?  Or maybe I misunderstood the motivation.

> Another approach we could consider is keeping exec.c's one-off approach
> to error handling and letting it redefine pg_prevent_errno_in_scope() as
> empty.  But that's ugly.
>
> Or we could make the affected call sites work like this:
>
>         int save_errno = errno;
>
>         log_error(_("could not identify current directory: %s"),
>                   strerror(save_errno));
>
> which on the whole might be the most expedient thing.

That was what I was going to propose, until I started wondering why we
need to do anything here.

-- 
Thomas Munro
http://www.enterprisedb.com


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Thomas Munro <thomas.munro@enterprisedb.com> writes:
> On Sun, May 27, 2018 at 4:21 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> (Basically what this would protect against is elog_start changing errno,
>> which it doesn't.)

> Hmm.  It looks like errstart() preserves errno to protect %m not from
> itself, but from the caller's other arguments to the elog facility.
> That seems reasonable, but do we really need to prohibit direct use of
> errno in expressions?  The only rogue actor likely to trash errno is
> you, the caller.  I mean, if you call elog(LOG, "foo %d %d", errno,
> fsync(bar)) it's obviously UB and your own fault, but who would do
> anything like that?  Or maybe I misunderstood the motivation.

Right, the concern is really about things like

    elog(..., f(x), strerror(errno));

If f() trashes errno --- perhaps only some of the time --- then this
is problematic.  It's especially problematic because whether f() is
evaluated before or after strerror() is platform-dependent.  So even
if the original author had tested things thoroughly, it might break
for somebody else.

The cases in exec.c all seem safe enough, but we have lots of examples
in the backend where we call nontrivial functions in the arguments of
elog/ereport.  It doesn't take much to make one nontrivial either.  If
memory serves, malloc() can trash errno on some platforms such as macOS,
so even just a palloc creates a hazard of a hard-to-reproduce problem.

At least in the case of ereport, all it takes to create a hazard is
more than one sub-function, eg this is risky:

    ereport(..., errmsg(..., strerror(errno)), errdetail(...));

because errdetail() might run first and malloc some memory for its
constructed string.

So I think a blanket policy of "don't trust errno within the arguments"
is a good idea, even though it might be safe to violate it in the
existing cases in exec.c.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> ... It doesn't take much to make one nontrivial either.  If
> memory serves, malloc() can trash errno on some platforms such as macOS,
> so even just a palloc creates a hazard of a hard-to-reproduce problem.

After digging around in the archives, the closest thing that we seem to
know for certain is that some versions of free() can trash errno, cf

https://www.postgresql.org/message-id/flat/E1UcmpR-0004nn-2i%40wrigleys.postgresql.org

as a result of possibly calling madvise() which might or might not
succeed.  But in the light of that knowledge, how much do you want
to bet that malloc() can't change errno?  And there's the possibility
that a called function does both a palloc and a pfree ...

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Thomas Munro
Дата:
On Sun, May 27, 2018 at 12:38 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> At least in the case of ereport, all it takes to create a hazard is
> more than one sub-function, eg this is risky:
>
>         ereport(..., errmsg(..., strerror(errno)), errdetail(...));
>
> because errdetail() might run first and malloc some memory for its
> constructed string.
>
> So I think a blanket policy of "don't trust errno within the arguments"
> is a good idea, even though it might be safe to violate it in the
> existing cases in exec.c.

Right, malloc() is a hazard I didn't think about.  I see that my local
malloc() makes an effort to save and restore errno around syscalls,
but even if all allocators were so thoughtful, which apparently they
aren't, there is also the problem that malloc itself can deliberately
set errno to ENOMEM per spec.  I take your more general point that you
can't rely on anything we didn't write not trashing errno, even libc.

On Tue, May 22, 2018 at 4:03 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I noticed another can of worms here, too: on Windows, doesn't use of
> GetLastError() in elog/ereport have exactly the same hazard as errno?
> Or is there some reason to think it can't change value during errstart()?

Yeah, on Windows the same must apply, not in errstart() itself but any
time you pass more than one value to elog() using expressions that
call functions we can't audit for last-error-stomping.

Out of curiosity I tried adding a GetLastError variable for Windows
(to hide the function of that name and break callers) to the earlier
experimental patch (attached).  I had to give it an initial value to
get rid of a warning about an unused variable (by my reading of the
documentation, __pragma(warning(suppress:4101)) can be used in macros
(unlike #pragma) and should shut that warning up, but it doesn't work
for me, not sure why).  Of course that produces many errors since we
do that all over the place:

https://ci.appveyor.com/project/macdice/postgres/build/1.0.184

-- 
Thomas Munro
http://www.enterprisedb.com

Вложения

Re: Allowing printf("%m") only where it actually works

От
Alvaro Herrera
Дата:
On 2018-May-27, Thomas Munro wrote:

> Out of curiosity I tried adding a GetLastError variable for Windows
> (to hide the function of that name and break callers) to the earlier
> experimental patch (attached).  I had to give it an initial value to
> get rid of a warning about an unused variable (by my reading of the
> documentation, __pragma(warning(suppress:4101)) can be used in macros
> (unlike #pragma) and should shut that warning up, but it doesn't work
> for me, not sure why).  Of course that produces many errors since we
> do that all over the place:
> 
> https://ci.appveyor.com/project/macdice/postgres/build/1.0.184

Ouch.

This seems to say that we oughta assign GetLastError() to saved_errno
during errstart, then use %m in the errmsg() instead.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> This seems to say that we oughta assign GetLastError() to saved_errno
> during errstart, then use %m in the errmsg() instead.

No, because in some parts of the code, errno does mean something,
even in Windows builds.

I think the right fix is to leave %m alone, and instead:

(1) save GetLastError's result along with errno during errstart.

(2) provide a function, say errlastwinerror(), to be used where
elog/ereport calls currently call GetLastError():

    elog(ERROR, "something went wrong: error code %lu",
         errlastwinerror());

What it does, of course, is to reach into the current error stack
level and retrieve the saved result.

We could use some hack along the line of what Thomas suggested
to enforce that this function is used rather than GetLastError().

It's amusing to speculate about whether we could actually cause
GetLastError() appearing within elog/ereport to get mapped into
this function, thus removing the need to change any code in call
sites.  But I suspect there's no adequately compiler-independent
way to do that.

I wonder whether we need to back-patch this.  I don't recall
seeing any field reports of misleading Windows error codes,
but I wonder how many people even look those up ...

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
In the hopes of getting the cfbot un-stuck (it's currently trying to
test a known-not-to-work patch), here are updated versions of the two
live patches we have in this thread.

0001 is the patch I originally proposed to adjust printf archetypes.

0002 is Thomas's patch to blow up on errno references in ereport/elog,
plus changes in src/common/exec.c to prevent that from blowing up.
(I went with the minimum-footprint way, for now; making exec.c's
error handling generally nicer seems like a task for another day.)

I think 0002 is probably pushable, really.  The unresolved issue about
0001 is whether it will create a spate of warnings on Windows builds,
and if so what to do about it.  We might learn something from the
cfbot about that, but I think the full buildfarm is going to be the
only really authoritative answer.

There's also the matter of providing similar safety for GetLastError
calls, but I think that deserves to be a separate patch ... and I don't
really want to take point on it since I lack a Windows machine.

            regards, tom lane

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 9731a51..5e56ca5 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 19,28 ****

  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
! # Set the format archetype used by gcc to check printf type functions.  We
! # prefer "gnu_printf", which includes what glibc uses, such as %m for error
! # strings and %lld for 64 bit long longs.  GCC 4.4 introduced it.  It makes a
! # dramatic difference on Windows.
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
--- 19,28 ----

  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
! # Set the format archetype used by gcc to check elog/ereport functions.
! # This should accept %m, whether or not the platform's printf does.
! # We use "gnu_printf" if possible, which does that, although in some cases
! # it might do more than we could wish.
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
*************** __attribute__((format(gnu_printf, 2, 3))
*** 34,41 ****
                    [pgac_cv_printf_archetype=gnu_printf],
                    [pgac_cv_printf_archetype=printf])
  ac_c_werror_flag=$ac_save_c_werror_flag])
! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
!                    [Define to gnu_printf if compiler supports it, else printf.])
  ])# PGAC_PRINTF_ARCHETYPE


--- 34,41 ----
                    [pgac_cv_printf_archetype=gnu_printf],
                    [pgac_cv_printf_archetype=printf])
  ac_c_werror_flag=$ac_save_c_werror_flag])
! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE_M], [$pgac_cv_printf_archetype],
!                    [Define as a format archetype that accepts %m, if available, else printf.])
  ])# PGAC_PRINTF_ARCHETYPE


diff --git a/configure b/configure
index 2665213..d4b4742 100755
*** a/configure
--- b/configure
*************** fi
*** 13394,13400 ****
  $as_echo "$pgac_cv_printf_archetype" >&6; }

  cat >>confdefs.h <<_ACEOF
! #define PG_PRINTF_ATTRIBUTE $pgac_cv_printf_archetype
  _ACEOF


--- 13394,13400 ----
  $as_echo "$pgac_cv_printf_archetype" >&6; }

  cat >>confdefs.h <<_ACEOF
! #define PG_PRINTF_ATTRIBUTE_M $pgac_cv_printf_archetype
  _ACEOF


diff --git a/src/include/c.h b/src/include/c.h
index 1e50103..0a4757e 100644
*** a/src/include/c.h
--- b/src/include/c.h
***************
*** 126,135 ****
  /* GCC and XLC support format attributes */
  #if defined(__GNUC__) || defined(__IBMC__)
  #define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
! #define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a)))
  #else
  #define pg_attribute_format_arg(a)
  #define pg_attribute_printf(f,a)
  #endif

  /* GCC, Sunpro and XLC support aligned, packed and noreturn */
--- 126,139 ----
  /* GCC and XLC support format attributes */
  #if defined(__GNUC__) || defined(__IBMC__)
  #define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
! /* Use for functions wrapping stdio's printf, which often doesn't take %m: */
! #define pg_attribute_printf(f,a) __attribute__((format(printf, f, a)))
! /* Use for elog/ereport, which implement %m for themselves: */
! #define pg_attribute_printf_m(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE_M, f, a)))
  #else
  #define pg_attribute_format_arg(a)
  #define pg_attribute_printf(f,a)
+ #define pg_attribute_printf_m(f,a)
  #endif

  /* GCC, Sunpro and XLC support aligned, packed and noreturn */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b7e4696..05775a3 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 809,816 ****
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define to gnu_printf if compiler supports it, else printf. */
! #undef PG_PRINTF_ATTRIBUTE

  /* PostgreSQL version as a string */
  #undef PG_VERSION
--- 809,816 ----
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define as a format archetype that accepts %m, if available, else printf. */
! #undef PG_PRINTF_ATTRIBUTE_M

  /* PostgreSQL version as a string */
  #undef PG_VERSION
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7a9ba7f..4f4091d 100644
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
*************** extern int    errcode(int sqlerrcode);
*** 133,157 ****
  extern int    errcode_for_file_access(void);
  extern int    errcode_for_socket_access(void);

! extern int    errmsg(const char *fmt,...) pg_attribute_printf(1, 2);
! extern int    errmsg_internal(const char *fmt,...) pg_attribute_printf(1, 2);

  extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural,
!               unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);

! extern int    errdetail(const char *fmt,...) pg_attribute_printf(1, 2);
! extern int    errdetail_internal(const char *fmt,...) pg_attribute_printf(1, 2);

! extern int    errdetail_log(const char *fmt,...) pg_attribute_printf(1, 2);

  extern int errdetail_log_plural(const char *fmt_singular,
                       const char *fmt_plural,
!                      unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);

  extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural,
!                  unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);

! extern int    errhint(const char *fmt,...) pg_attribute_printf(1, 2);

  /*
   * errcontext() is typically called in error context callback functions, not
--- 133,157 ----
  extern int    errcode_for_file_access(void);
  extern int    errcode_for_socket_access(void);

! extern int    errmsg(const char *fmt,...) pg_attribute_printf_m(1, 2);
! extern int    errmsg_internal(const char *fmt,...) pg_attribute_printf_m(1, 2);

  extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural,
!               unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

! extern int    errdetail(const char *fmt,...) pg_attribute_printf_m(1, 2);
! extern int    errdetail_internal(const char *fmt,...) pg_attribute_printf_m(1, 2);

! extern int    errdetail_log(const char *fmt,...) pg_attribute_printf_m(1, 2);

  extern int errdetail_log_plural(const char *fmt_singular,
                       const char *fmt_plural,
!                      unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

  extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural,
!                  unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4);

! extern int    errhint(const char *fmt,...) pg_attribute_printf_m(1, 2);

  /*
   * errcontext() is typically called in error context callback functions, not
*************** extern int    errhint(const char *fmt,...)
*** 165,171 ****

  extern int    set_errcontext_domain(const char *domain);

! extern int    errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2);

  extern int    errhidestmt(bool hide_stmt);
  extern int    errhidecontext(bool hide_ctx);
--- 165,171 ----

  extern int    set_errcontext_domain(const char *domain);

! extern int    errcontext_msg(const char *fmt,...) pg_attribute_printf_m(1, 2);

  extern int    errhidestmt(bool hide_stmt);
  extern int    errhidecontext(bool hide_ctx);
*************** extern int    getinternalerrposition(void);
*** 222,234 ****
  #endif                            /* HAVE__VA_ARGS */

  extern void elog_start(const char *filename, int lineno, const char *funcname);
! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf(2, 3);


  /* Support for constructing error strings separately from ereport() calls */

  extern void pre_format_elog_string(int errnumber, const char *domain);
! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2);


  /* Support for attaching context information to error reports */
--- 222,234 ----
  #endif                            /* HAVE__VA_ARGS */

  extern void elog_start(const char *filename, int lineno, const char *funcname);
! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf_m(2, 3);


  /* Support for constructing error strings separately from ereport() calls */

  extern void pre_format_elog_string(int errnumber, const char *domain);
! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf_m(1, 2);


  /* Support for attaching context information to error reports */
*************** extern void set_syslog_parameters(const
*** 407,415 ****
  #endif

  /*
!  * Write errors to stderr (or by equal means when stderr is
!  * not available). Used before ereport/elog can be used
!  * safely (memory context, GUC load etc)
   */
  extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);

--- 407,415 ----
  #endif

  /*
!  * Write errors to stderr (or by comparable means when stderr is not
!  * available).  Used before ereport/elog can be used safely (memory context,
!  * GUC load etc).  Note that this does *not* accept "%m".
   */
  extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);

diff --git a/src/common/exec.c b/src/common/exec.c
index 4df16cd..c207c02 100644
--- a/src/common/exec.c
+++ b/src/common/exec.c
@@ -124,8 +124,10 @@ find_my_exec(const char *argv0, char *retpath)

     if (!getcwd(cwd, MAXPGPATH))
     {
+        int            save_errno = errno;
+
         log_error(_("could not identify current directory: %s"),
-                  strerror(errno));
+                  strerror(save_errno));
         return -1;
     }

@@ -238,8 +240,10 @@ resolve_symlinks(char *path)
      */
     if (!getcwd(orig_wd, MAXPGPATH))
     {
+        int            save_errno = errno;
+
         log_error(_("could not identify current directory: %s"),
-                  strerror(errno));
+                  strerror(save_errno));
         return -1;
     }

@@ -254,7 +258,10 @@ resolve_symlinks(char *path)
             *lsep = '\0';
             if (chdir(path) == -1)
             {
-                log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
+                int            save_errno = errno;
+
+                log_error4(_("could not change directory to \"%s\": %s"),
+                           path, strerror(save_errno));
                 return -1;
             }
             fname = lsep + 1;
@@ -281,8 +288,10 @@ resolve_symlinks(char *path)

     if (!getcwd(path, MAXPGPATH))
     {
+        int            save_errno = errno;
+
         log_error(_("could not identify current directory: %s"),
-                  strerror(errno));
+                  strerror(save_errno));
         return -1;
     }
     join_path_components(path, path, link_buf);
@@ -290,7 +299,10 @@ resolve_symlinks(char *path)

     if (chdir(orig_wd) == -1)
     {
-        log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
+        int            save_errno = errno;
+
+        log_error4(_("could not change directory to \"%s\": %s"),
+                   orig_wd, strerror(save_errno));
         return -1;
     }
 #endif                            /* HAVE_READLINK */
@@ -520,7 +532,10 @@ pclose_check(FILE *stream)
     if (exitstatus == -1)
     {
         /* pclose() itself failed, and hopefully set errno */
-        log_error(_("pclose failed: %s"), strerror(errno));
+        int            save_errno = errno;
+
+        log_error(_("pclose failed: %s"),
+                  strerror(save_errno));
     }
     else
     {
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7a9ba7f..4350b12 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -70,6 +70,23 @@
 /* SQLSTATE codes for errors are defined in a separate file */
 #include "utils/errcodes.h"

+/*
+ * Provide a way to prevent "errno" from being accidentally used inside an
+ * elog() or ereport() invocation.  Since we know that some operating systems
+ * define errno as something involving a function call, we'll put a local
+ * variable of the same name as that function in the local scope to force a
+ * compile error.  On platforms that don't define errno in that way, nothing
+ * happens, so we get no warning ... but we can live with that as long as it
+ * happens on some popular platforms.
+ */
+#if defined(errno) && defined(__linux__)
+#define pg_prevent_errno_in_scope() int __errno_location pg_attribute_unused()
+#elif defined(errno) && (defined(__darwin__) || defined(__freebsd__))
+#define pg_prevent_errno_in_scope() int __error pg_attribute_unused()
+#else
+#define pg_prevent_errno_in_scope()
+#endif
+

 /*----------
  * New-style error reporting API: to be used in this way:
@@ -103,6 +120,7 @@
 #ifdef HAVE__BUILTIN_CONSTANT_P
 #define ereport_domain(elevel, domain, rest)    \
     do { \
+        pg_prevent_errno_in_scope(); \
         if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \
             errfinish rest; \
         if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \
@@ -112,6 +130,7 @@
 #define ereport_domain(elevel, domain, rest)    \
     do { \
         const int elevel_ = (elevel); \
+        pg_prevent_errno_in_scope(); \
         if (errstart(elevel_, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \
             errfinish rest; \
         if (elevel_ >= ERROR) \
@@ -198,6 +217,7 @@ extern int    getinternalerrposition(void);
 #ifdef HAVE__BUILTIN_CONSTANT_P
 #define elog(elevel, ...)  \
     do { \
+        pg_prevent_errno_in_scope(); \
         elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \
         elog_finish(elevel, __VA_ARGS__); \
         if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \
@@ -206,6 +226,7 @@ extern int    getinternalerrposition(void);
 #else                            /* !HAVE__BUILTIN_CONSTANT_P */
 #define elog(elevel, ...)  \
     do { \
+        pg_prevent_errno_in_scope(); \
         elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \
         { \
             const int elevel_ = (elevel); \

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> I think 0002 is probably pushable, really.  The unresolved issue about
> 0001 is whether it will create a spate of warnings on Windows builds,
> and if so what to do about it.  We might learn something from the
> cfbot about that, but I think the full buildfarm is going to be the
> only really authoritative answer.

Ah, cfbot has a run already, and it reports no warnings or errors in
its Windows build.

At this point I'm inclined to push both of those patches so we can
see what the buildfarm makes of them.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> At this point I'm inclined to push both of those patches so we can
> see what the buildfarm makes of them.

So I did that, and while not all of the buildfarm has reported in,
enough of it has that I think we can draw conclusions.  The only member
that's showing any new warnings, AFAICT, is jacana (gcc 4.9 on Windows).
It had no format-related warnings yesterday, but now it has a boatload
of 'em, and it appears that every single one traces to not believing
that printf and friends understand 'l' and 'z' length modifiers.

The reason for this seems to be that we unconditionally replace the
printf function family with snprintf.c on Windows, and port.h causes
those functions to be marked with pg_attribute_printf, which this
patch caused to mean just "printf" not "gnu_printf".  So this gcc
evidently thinks the platform printf doesn't know 'l' and 'z'
(which may or may not be true in reality, but it's irrelevant)
and it complains.

There are also interesting warnings showing up in elog.c, such as

Aug 11 14:26:32 c:/mingw/msys/1.0/home/pgrunner/bf/root/HEAD/pgsql.build/../pgsql/src/backend/utils/error/elog.c:807:2:
warning:function might be possible candidate for 'ms_printf' format attribute [-Wsuggest-attribute=format] 

I think what is happening here is that gcc notices that those functions
call appendStringInfoVA, which is now annotated with the printf
archetype not gnu_printf, so it decides that maybe we marked the elog.c
functions with the wrong archetype.  I have no idea why it's suggesting
"ms_printf" though --- I can find nothing on the web that even admits
that gcc knows such an archetype.

So this is all pretty much of a mess.  If we annotate the elog functions
differently from printf's annotation then we risk getting these complaints
in elog.c, but if we don't do that then we can't really describe their
semantics correctly.  We could possibly mark the replacement snprintf
functions with gnu_printf, but that's a lie with respect to the very
point at issue about %m.  Unless we were to teach snprintf.c about %m
... which probably wouldn't be hard, but I'm not sure I want to go there.
That line of thought leads to deciding that we should treat "printf
doesn't know %m" as a reason to use snprintf.c over the native printf;
and I think we probably do not want to do that, if only because the
native printf is probably more efficient than snprintf.c.  (There are
other reasons to question that too: we probably can't tell without a
run-time test in configure, and even if we detect it correctly, gcc
might be misconfigured to believe the opposite thing about %m support
and hence warn, or fail to warn, anyway.  clang at least seems to get
this wrong frequently.)  But if we do not do such replacement then we
still end up wondering how to mark printf wrapper functions such as
appendStringInfoVA.

At this point I'm inclined to give up and revert 3a60c8ff8.  It's not
clear that we can really make the warning situation better, as opposed
to just moving the warnings from one platform to another.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Fabien COELHO
Дата:
> [...]
>
> At this point I'm inclined to give up and revert 3a60c8ff8.  It's not
> clear that we can really make the warning situation better, as opposed
> to just moving the warnings from one platform to another.

Indeed, there are hundreds of warnings around "pg_printf_attribute_m" 
added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff.

A revert would help.

-- 
Fabien.


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Fabien COELHO <coelho@cri.ensmp.fr> writes:
> Indeed, there are hundreds of warnings around "pg_printf_attribute_m" 
> added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff.

Oh?  What warnings exactly?  I would not expect any new warnings except
on platforms where gcc believes the local printf is non POSIX compliant,
which certainly ought not happen on any non-obsolete Linux.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> So this is all pretty much of a mess.  If we annotate the elog functions
> differently from printf's annotation then we risk getting these complaints
> in elog.c, but if we don't do that then we can't really describe their
> semantics correctly.  We could possibly mark the replacement snprintf
> functions with gnu_printf, but that's a lie with respect to the very
> point at issue about %m.  Unless we were to teach snprintf.c about %m
> ... which probably wouldn't be hard, but I'm not sure I want to go there.

Actually ... the more I think about this, the less insane that idea seems.
Consider the following approach:

1. Teach src/port/snprintf.c about %m.  While I've not written a patch
for this, it looks pretty trivial.

2. Teach configure to test for %m and if it's not there, use the
replacement snprintf.  (Note: we're already forcing snprintf replacement
in cross-compiles, so the added run-time test isn't losing anything.)

3. Get rid of elog.c's hand-made substitution of %m strings, and instead
just let it pass the correct errno value down.  (We'd likely need to do
some fooling in appendStringInfoVA and related functions to preserve
call-time errno, but that's not complicated, nor expensive.)

4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in
frontend code.

Once we've done this, we have uniform printf semantics across all
platforms, which is kind of nice from a testing standpoint, as well as
being less of a cognitive load for developers.  And we can stick with
the existing approach of using the gnu_printf archetype across the board;
that's no longer a lie for the snprintf.c code.

One objection to this is the possible performance advantage of the native
printf functions over snprintf.c.  I did a bit of investigation of that
using the attached testbed, and found that the quality of implementation
of the native functions seems pretty variable:

RHEL6's glibc on x86_64 (this is just a comparison point, since we'd not
be replacing glibc's printf anyway):

snprintf time = 756.795 ms total, 0.000756795 ms per iteration
pg_snprintf time = 824.643 ms total, 0.000824643 ms per iteration

macOS High Sierra on x86_64:

snprintf time = 264.071 ms total, 0.000264071 ms per iteration
pg_snprintf time = 348.41 ms total, 0.00034841 ms per iteration

FreeBSD 11.0 on x86_64:

snprintf time = 628.873 ms total, 0.000628873 ms per iteration
pg_snprintf time = 606.684 ms total, 0.000606684 ms per iteration

OpenBSD 6.0 on x86_64 (same hardware as FreeBSD test):

snprintf time = 331.245 ms total, 0.000331245 ms per iteration
pg_snprintf time = 539.849 ms total, 0.000539849 ms per iteration

NetBSD 8.99 on armv6:

snprintf time = 2423.39 ms total, 0.00242339 ms per iteration
pg_snprintf time = 3769.16 ms total, 0.00376916 ms per iteration

So we would be taking a hit on most platforms, but I've not really
seen sprintf as a major component of very many profiles.  Moreover,
at least for the elog/ereport use-case, we'd be buying back some
nontrivial part of that hit by getting rid of expand_fmt_string().
Also worth noting is that we've never made any effort at all to
micro-optimize snprintf.c, so maybe there's some gold to be mined
there to reduce the penalty.

A different objection, possibly more serious than the performance
one, is that if we get in the habit of using %m in frontend code
then at some point we'd inevitably back-patch such a usage.  (Worse,
it'd pass testing on glibc platforms, only to fail elsewhere.)
I don't see a bulletproof answer to that except to back-patch this
set of changes, which might be a bridge too far.

Aside from the back-patching angle, though, this seems pretty
promising.  Thoughts?

            regards, tom lane

PS: here's the testbed I used for the above numbers.  Feel free to
try other platforms or other test-case formats.  Compile this with
something like

gcc -Wall -O2 -I pgsql/src/include -I pgsql/src/port timeprintf.c

(point the -I switches into a configured PG source tree); you might
need to add "-lrt" or some such to get clock_gettime().  Then run
with "./a.out 1000000" or so.

#include "postgres_fe.h"

#include "portability/instr_time.h"

#include "snprintf.c"

int
main(int argc, char **argv)
{
    int count = 0;
    char buffer[1000];
    instr_time    start;
    instr_time    stop;
    double elapsed;
    int i;

    if (argc > 1)
        count = atoi(argv[1]);
    if (count <= 0)
        count = 1000000;

    INSTR_TIME_SET_CURRENT(start);

    for (i = 0; i < count; i++)
    {
        snprintf(buffer, sizeof(buffer),
                 "%d %g %s",
                 42, 42.2,
"01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
    }

    INSTR_TIME_SET_CURRENT(stop);
    INSTR_TIME_SUBTRACT(stop, start);
    elapsed = INSTR_TIME_GET_MILLISEC(stop);

    printf("snprintf time = %g ms total, %g ms per iteration\n",
           elapsed, elapsed / count);

    INSTR_TIME_SET_CURRENT(start);

    for (i = 0; i < count; i++)
    {
        pg_snprintf(buffer, sizeof(buffer),
                 "%d %g %s",
                 42, 42.2,
"01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
    }

    INSTR_TIME_SET_CURRENT(stop);
    INSTR_TIME_SUBTRACT(stop, start);
    elapsed = INSTR_TIME_GET_MILLISEC(stop);

    printf("pg_snprintf time = %g ms total, %g ms per iteration\n",
           elapsed, elapsed / count);

    return 0;
}

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> ... So we would be taking a hit on most platforms, but I've not really
> seen sprintf as a major component of very many profiles.  Moreover,
> at least for the elog/ereport use-case, we'd be buying back some
> nontrivial part of that hit by getting rid of expand_fmt_string().
> Also worth noting is that we've never made any effort at all to
> micro-optimize snprintf.c, so maybe there's some gold to be mined
> there to reduce the penalty.

Oh, the plot thickens: apparently, a very large chunk of the time
in that test scenario went into the %g format item, which I think
we can all agree isn't performance-critical for Postgres.  Changing
the test case to test %lld in place of %g, I get (on the same
five platforms as before)

RHEL6:

snprintf time = 357.634 ms total, 0.000357634 ms per iteration
pg_snprintf time = 281.708 ms total, 0.000281708 ms per iteration
ratio = 0.788

macOS:

snprintf time = 155.86 ms total, 0.00015586 ms per iteration
pg_snprintf time = 104.074 ms total, 0.000104074 ms per iteration
ratio = 0.668

FreeBSD:

snprintf time = 268.883 ms total, 0.000268883 ms per iteration
pg_snprintf time = 185.294 ms total, 0.000185294 ms per iteration
ratio = 0.689

OpenBSD:

snprintf time = 276.418 ms total, 0.000276418 ms per iteration
pg_snprintf time = 153.334 ms total, 0.000153334 ms per iteration
ratio = 0.555

NetBSD:

snprintf time = 1174.13 ms total, 0.00117413 ms per iteration
pg_snprintf time = 1508.82 ms total, 0.00150882 ms per iteration
ratio = 1.285

So there's actually a plausible argument to be made that we'd
get a net speed win on most platforms and test cases.

            regards, tom lane

#include "postgres_fe.h"

#include "portability/instr_time.h"

#include "snprintf.c"

int
main(int argc, char **argv)
{
    int count = 0;
    char buffer[1000];
    instr_time    start;
    instr_time    stop;
    double elapsed;
    double elapsed2;
    int i;

    if (argc > 1)
        count = atoi(argv[1]);
    if (count <= 0)
        count = 1000000;

    INSTR_TIME_SET_CURRENT(start);

    for (i = 0; i < count; i++)
    {
        snprintf(buffer, sizeof(buffer),
                 "%d %lld %s",
                 42, (long long) 42,

"01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
    }

    INSTR_TIME_SET_CURRENT(stop);
    INSTR_TIME_SUBTRACT(stop, start);
    elapsed = INSTR_TIME_GET_MILLISEC(stop);

    printf("snprintf time = %g ms total, %g ms per iteration\n",
           elapsed, elapsed / count);

    INSTR_TIME_SET_CURRENT(start);

    for (i = 0; i < count; i++)
    {
        pg_snprintf(buffer, sizeof(buffer),
                 "%d %lld %s",
                 42, (long long) 42,

"01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
    }

    INSTR_TIME_SET_CURRENT(stop);
    INSTR_TIME_SUBTRACT(stop, start);
    elapsed2 = INSTR_TIME_GET_MILLISEC(stop);

    printf("pg_snprintf time = %g ms total, %g ms per iteration\n",
           elapsed2, elapsed2 / count);
    printf("ratio = %.3f\n", elapsed2 / elapsed);

    return 0;
}

Re: Allowing printf("%m") only where it actually works

От
Robert Haas
Дата:
On Sun, Aug 12, 2018 at 3:08 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Moreover,
> at least for the elog/ereport use-case, we'd be buying back some
> nontrivial part of that hit by getting rid of expand_fmt_string().

Yeah.  I think expand_fmt_string() is pretty costly if you are doing a
lot of errors (e.g. write a function that uses an EXCEPTION block to
map ERROR -> NULL return and then do SELECT catch_errors(blah) FROM
generate_series(1,10000000) g or so.  It seems altogether likely to me
that getting rid of the need for expand_fmt_string() will make for a
net win.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Re: Allowing printf("%m") only where it actually works

От
Fabien COELHO
Дата:
>> Indeed, there are hundreds of warnings around "pg_printf_attribute_m"
>> added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff.
>
> Oh?  What warnings exactly?  I would not expect any new warnings except
> on platforms where gcc believes the local printf is non POSIX compliant,
> which certainly ought not happen on any non-obsolete Linux.

Hmmm. Strange. The good news, is sthat it does not show anymore. Maybe 
this was because I had not done a "configure" before recompiling. Sorry 
for the noise.

-- 
Fabien.


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> Consider the following approach:
> 1. Teach src/port/snprintf.c about %m.  While I've not written a patch
> for this, it looks pretty trivial.
> 2. Teach configure to test for %m and if it's not there, use the
> replacement snprintf.  (Note: we're already forcing snprintf replacement
> in cross-compiles, so the added run-time test isn't losing anything.)
> 3. Get rid of elog.c's hand-made substitution of %m strings, and instead
> just let it pass the correct errno value down.  (We'd likely need to do
> some fooling in appendStringInfoVA and related functions to preserve
> call-time errno, but that's not complicated, nor expensive.)
> 4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in
> frontend code.

So I started to hack on this, and soon noticed that actually, what elog.c
replaces %m with is *not* the result of strerror(), it's the result of
useful_strerror().  Which, primarily, does this:

    /*
     * Some strerror()s return an empty string for out-of-range errno.  This
     * is ANSI C spec compliant, but not exactly useful.  Also, we may get
     * back strings of question marks if libc cannot transcode the message to
     * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
     * get_errno_symbol(), and if that fails, print the numeric errno.
     */

I don't know offhand whether glibc's implementation delivers anything
useful for out-of-range errno, but I do know that we've seen the
transcoding problem with it, cf commit 8e68816cc which arose from
this discussion:

https://www.postgresql.org/message-id/flat/2782A2665E8342DF8695F396DBA80C88%40maumau

We could easily move useful_strerror() into snprintf.c, I think
(might need to move pgwin32_socket_strerror there too).  But then
we'd lose its functionality when using glibc.

So now I'm about ready to propose that we just *always* use
snprintf.c, and forget all of the related configure probing.
This'd have some advantages, notably that we'd get the
useful_strerror() behavior in frontend as well as backend,
assuming we converted all our frontend code to use %m.
And we'd not exactly be the first project to decide that.
But it's kind of a big move from where we are today.

Thoughts?

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Nico Williams
Дата:
On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote:
> So now I'm about ready to propose that we just *always* use
> snprintf.c, and forget all of the related configure probing.

Yes.

> This'd have some advantages, notably that we'd get the
> useful_strerror() behavior in frontend as well as backend,
> assuming we converted all our frontend code to use %m.

You'd also get to ensure that all uses from *die() are
async-signal-safe.

You'd also ensure that snprintf.c gets maximal testing.

> And we'd not exactly be the first project to decide that.
> But it's kind of a big move from where we are today.
> 
> Thoughts?

I think that is the best approach.


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Nico Williams <nico@cryptonector.com> writes:
> On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote:
>> So now I'm about ready to propose that we just *always* use
>> snprintf.c, and forget all of the related configure probing.

> You'd also get to ensure that all uses from *die() are
> async-signal-safe.

[ raised eyebrow... ] That seems like more than I care to promise
here.  But even if snprintf itself were unconditionally safe,
there's plenty of other stuff in that code path that isn't.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Nico Williams
Дата:
On Sun, Aug 19, 2018 at 01:15:58AM -0400, Tom Lane wrote:
> Nico Williams <nico@cryptonector.com> writes:
> > On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote:
> >> So now I'm about ready to propose that we just *always* use
> >> snprintf.c, and forget all of the related configure probing.
> 
> > You'd also get to ensure that all uses from *die() are
> > async-signal-safe.
> 
> [ raised eyebrow... ] That seems like more than I care to promise
> here.  But even if snprintf itself were unconditionally safe,
> there's plenty of other stuff in that code path that isn't.

One step at a time, no?  And there's the other benefits.


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
>> Consider the following approach:
>> 1. Teach src/port/snprintf.c about %m.  While I've not written a patch
>> for this, it looks pretty trivial.
>> 2. Teach configure to test for %m and if it's not there, use the
>> replacement snprintf.  (Note: we're already forcing snprintf replacement
>> in cross-compiles, so the added run-time test isn't losing anything.)
>> 3. Get rid of elog.c's hand-made substitution of %m strings, and instead
>> just let it pass the correct errno value down.  (We'd likely need to do
>> some fooling in appendStringInfoVA and related functions to preserve
>> call-time errno, but that's not complicated, nor expensive.)
>> 4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in
>> frontend code.

> So I started to hack on this, and soon noticed that actually, what elog.c
> replaces %m with is *not* the result of strerror(), it's the result of
> useful_strerror().

After further thought, I realized that the best way to handle that is to
turn useful_strerror() into a globally available wrapper pg_strerror()
that replaces strerror() everyplace.  That way we get its protections in
frontend code as well as backend, and we ensure that the results of
printing strerror(errno) match what %m would do (so that step 4 above is
just cosmetic code simplification and doesn't change behavior).  We'd
speculated about doing that back when 8e68816cc went in, but not actually
pulled the trigger.

So the first attached patch does that, and then the second one implements
%m in snprintf.c and causes it to be used all the time.  I've not touched
step 4 yet, that could be done later/piecemeal.

Although the attached causes strerror.c to be included in libpq, I think
it's actually dead code at the moment, because on any reasonably modern
platform (including *all* of the buildfarm) libpq does not depend on
strerror but strerror_r, cf pqStrerror().  It's tempting to expand
strerror.c to include a similar wrapper for strerror_r, so that the
extra functionality exists for libpq's usage too.  (Also, it'd likely
be better for snprintf.c to depend on strerror_r not strerror, to avoid
unnecessary thread-unsafety.)  But I've left that for later.

A couple of additional notes for review:

* The 0002 patch will conflict with my snprintf-speedup patch, but
resolving that is simple (just need to move one of the %m hunks around).

* src/port/strerror.c already exists, but as far as I can see it's been
dead code for decades; no ANSI-C-compliant platform lacks strerror()
altogether.  Moreover, ecpg never got taught to include it, so obviously
we've not built on a platform with that problem anytime recently.
So I just removed the former contents of that file.

* The most nervous-making aspect of this patch, IMO, is that there's an
addition to the API spec for appendStringInfoVA and pvsnprintf: callers
have to preserve errno when looping.  Fortunately there are very few
direct callers of those, but I'm slightly worried that extensions might
do so.  I don't see any way to avoid that change though.

* I dropped configure's checks for existence/declaration of snprintf
and vsnprintf, since (a) we no longer care, and (b) those are pretty
much useless anyway; no active buildfarm member fails those checks.

* The Windows aspects of this are untested.  It seems like importing
pgwin32_socket_strerror's behavior into the frontend ought to be a
bug fix, though: win32_port.h redefines socket error symbols whether
FRONTEND is set or not, so aren't we printing bogus info for socket
errors in frontend right now?

            regards, tom lane

diff --git a/configure b/configure
index 836d68d..fadd06e 100755
*** a/configure
--- b/configure
*************** esac
*** 15537,15555 ****

  fi

- ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
- if test "x$ac_cv_func_strerror" = xyes; then :
-   $as_echo "#define HAVE_STRERROR 1" >>confdefs.h
-
- else
-   case " $LIBOBJS " in
-   *" strerror.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS strerror.$ac_objext"
-  ;;
- esac
-
- fi
-
  ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
  if test "x$ac_cv_func_strlcat" = xyes; then :
    $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
--- 15537,15542 ----
diff --git a/configure.in b/configure.in
index 6e14106..3adec10 100644
*** a/configure.in
--- b/configure.in
*************** else
*** 1649,1655 ****
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt fls getopt getrusage inet_aton mkdtemp random rint srandom strerror strlcat strlcpy strnlen])

  case $host_os in

--- 1649,1655 ----
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt fls getopt getrusage inet_aton mkdtemp random rint srandom strlcat strlcpy strnlen])

  case $host_os in

diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index f4356fe..af35cfb 100644
*** a/src/backend/port/win32/socket.c
--- b/src/backend/port/win32/socket.c
*************** pgwin32_select(int nfds, fd_set *readfds
*** 690,728 ****
          memcpy(writefds, &outwritefds, sizeof(fd_set));
      return nummatches;
  }
-
-
- /*
-  * Return win32 error string, since strerror can't
-  * handle winsock codes
-  */
- static char wserrbuf[256];
- const char *
- pgwin32_socket_strerror(int err)
- {
-     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
-
-     if (handleDLL == INVALID_HANDLE_VALUE)
-     {
-         handleDLL = LoadLibraryEx("netmsg.dll", NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
-         if (handleDLL == NULL)
-             ereport(FATAL,
-                     (errmsg_internal("could not load netmsg.dll: error code %lu", GetLastError())));
-     }
-
-     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
-     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
-                       FORMAT_MESSAGE_FROM_SYSTEM |
-                       FORMAT_MESSAGE_FROM_HMODULE,
-                       handleDLL,
-                       err,
-                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
-                       wserrbuf,
-                       sizeof(wserrbuf) - 1,
-                       NULL) == 0)
-     {
-         /* Failed to get id */
-         sprintf(wserrbuf, "unrecognized winsock error %d", err);
-     }
-     return wserrbuf;
- }
--- 690,692 ----
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 16531f7..22e5d87 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void send_message_to_server_log(E
*** 178,185 ****
  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 *useful_strerror(int errnum);
- static const char *get_errno_symbol(int errnum);
  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);
--- 178,183 ----
*************** expand_fmt_string(const char *fmt, Error
*** 3360,3366 ****
                   */
                  const char *cp2;

!                 cp2 = useful_strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
--- 3358,3364 ----
                   */
                  const char *cp2;

!                 cp2 = strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
*************** expand_fmt_string(const char *fmt, Error
*** 3384,3602 ****


  /*
-  * A slightly cleaned-up version of strerror()
-  */
- static const char *
- useful_strerror(int errnum)
- {
-     /* this buffer is only used if strerror() and get_errno_symbol() fail */
-     static char errorstr_buf[48];
-     const char *str;
-
- #ifdef WIN32
-     /* Winsock error code range, per WinError.h */
-     if (errnum >= 10000 && errnum <= 11999)
-         return pgwin32_socket_strerror(errnum);
- #endif
-     str = strerror(errnum);
-
-     /*
-      * Some strerror()s return an empty string for out-of-range errno.  This
-      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
-      * back strings of question marks if libc cannot transcode the message to
-      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
-      * get_errno_symbol(), and if that fails, print the numeric errno.
-      */
-     if (str == NULL || *str == '\0' || *str == '?')
-         str = get_errno_symbol(errnum);
-
-     if (str == NULL)
-     {
-         snprintf(errorstr_buf, sizeof(errorstr_buf),
-         /*------
-           translator: This string will be truncated at 47
-           characters expanded. */
-                  _("operating system error %d"), errnum);
-         str = errorstr_buf;
-     }
-
-     return str;
- }
-
- /*
-  * Returns a symbol (e.g. "ENOENT") for an errno code.
-  * Returns NULL if the code is unrecognized.
-  */
- static const char *
- get_errno_symbol(int errnum)
- {
-     switch (errnum)
-     {
-         case E2BIG:
-             return "E2BIG";
-         case EACCES:
-             return "EACCES";
- #ifdef EADDRINUSE
-         case EADDRINUSE:
-             return "EADDRINUSE";
- #endif
- #ifdef EADDRNOTAVAIL
-         case EADDRNOTAVAIL:
-             return "EADDRNOTAVAIL";
- #endif
-         case EAFNOSUPPORT:
-             return "EAFNOSUPPORT";
- #ifdef EAGAIN
-         case EAGAIN:
-             return "EAGAIN";
- #endif
- #ifdef EALREADY
-         case EALREADY:
-             return "EALREADY";
- #endif
-         case EBADF:
-             return "EBADF";
- #ifdef EBADMSG
-         case EBADMSG:
-             return "EBADMSG";
- #endif
-         case EBUSY:
-             return "EBUSY";
-         case ECHILD:
-             return "ECHILD";
- #ifdef ECONNABORTED
-         case ECONNABORTED:
-             return "ECONNABORTED";
- #endif
-         case ECONNREFUSED:
-             return "ECONNREFUSED";
- #ifdef ECONNRESET
-         case ECONNRESET:
-             return "ECONNRESET";
- #endif
-         case EDEADLK:
-             return "EDEADLK";
-         case EDOM:
-             return "EDOM";
-         case EEXIST:
-             return "EEXIST";
-         case EFAULT:
-             return "EFAULT";
-         case EFBIG:
-             return "EFBIG";
- #ifdef EHOSTUNREACH
-         case EHOSTUNREACH:
-             return "EHOSTUNREACH";
- #endif
-         case EIDRM:
-             return "EIDRM";
-         case EINPROGRESS:
-             return "EINPROGRESS";
-         case EINTR:
-             return "EINTR";
-         case EINVAL:
-             return "EINVAL";
-         case EIO:
-             return "EIO";
- #ifdef EISCONN
-         case EISCONN:
-             return "EISCONN";
- #endif
-         case EISDIR:
-             return "EISDIR";
- #ifdef ELOOP
-         case ELOOP:
-             return "ELOOP";
- #endif
-         case EMFILE:
-             return "EMFILE";
-         case EMLINK:
-             return "EMLINK";
-         case EMSGSIZE:
-             return "EMSGSIZE";
-         case ENAMETOOLONG:
-             return "ENAMETOOLONG";
-         case ENFILE:
-             return "ENFILE";
-         case ENOBUFS:
-             return "ENOBUFS";
-         case ENODEV:
-             return "ENODEV";
-         case ENOENT:
-             return "ENOENT";
-         case ENOEXEC:
-             return "ENOEXEC";
-         case ENOMEM:
-             return "ENOMEM";
-         case ENOSPC:
-             return "ENOSPC";
-         case ENOSYS:
-             return "ENOSYS";
- #ifdef ENOTCONN
-         case ENOTCONN:
-             return "ENOTCONN";
- #endif
-         case ENOTDIR:
-             return "ENOTDIR";
- #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
-         case ENOTEMPTY:
-             return "ENOTEMPTY";
- #endif
- #ifdef ENOTSOCK
-         case ENOTSOCK:
-             return "ENOTSOCK";
- #endif
- #ifdef ENOTSUP
-         case ENOTSUP:
-             return "ENOTSUP";
- #endif
-         case ENOTTY:
-             return "ENOTTY";
-         case ENXIO:
-             return "ENXIO";
- #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
-         case EOPNOTSUPP:
-             return "EOPNOTSUPP";
- #endif
- #ifdef EOVERFLOW
-         case EOVERFLOW:
-             return "EOVERFLOW";
- #endif
-         case EPERM:
-             return "EPERM";
-         case EPIPE:
-             return "EPIPE";
-         case EPROTONOSUPPORT:
-             return "EPROTONOSUPPORT";
-         case ERANGE:
-             return "ERANGE";
- #ifdef EROFS
-         case EROFS:
-             return "EROFS";
- #endif
-         case ESRCH:
-             return "ESRCH";
- #ifdef ETIMEDOUT
-         case ETIMEDOUT:
-             return "ETIMEDOUT";
- #endif
- #ifdef ETXTBSY
-         case ETXTBSY:
-             return "ETXTBSY";
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-         case EWOULDBLOCK:
-             return "EWOULDBLOCK";
- #endif
-         case EXDEV:
-             return "EXDEV";
-     }
-
-     return NULL;
- }
-
-
- /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
--- 3382,3387 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 827574e..2861b1f 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 519,527 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #undef HAVE_STDLIB_H

- /* Define to 1 if you have the `strerror' function. */
- #undef HAVE_STRERROR
-
  /* Define to 1 if you have the `strerror_r' function. */
  #undef HAVE_STRERROR_R

--- 519,524 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 46ce49d..4c68fa8 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 390,400 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #define HAVE_STDLIB_H 1

- /* Define to 1 if you have the `strerror' function. */
- #ifndef HAVE_STRERROR
- #define HAVE_STRERROR 1
- #endif
-
  /* Define to 1 if you have the `strerror_r' function. */
  /* #undef HAVE_STRERROR_R */

--- 390,395 ----
diff --git a/src/include/port.h b/src/include/port.h
index 74a9dc4..dae80cb 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern int    pg_printf(const char *fmt,...
*** 189,194 ****
--- 189,198 ----
  #endif
  #endif                            /* USE_REPL_SNPRINTF */

+ /* Replace strerror() with our own, somewhat more robust wrapper */
+ extern char *pg_strerror(int errnum);
+ #define strerror pg_strerror
+
  /* Portable prompt handling */
  extern void simple_prompt(const char *prompt, char *destination, size_t destlen,
                bool echo);
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index b398cd3..360dbdf 100644
*** a/src/include/port/win32_port.h
--- b/src/include/port/win32_port.h
*************** extern int    pgwin32_safestat(const char *
*** 322,329 ****
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants.  This allows elog.c to recognize them as being in the Winsock
!  * error code range and pass them off to pgwin32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
--- 322,329 ----
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants. This allows strerror.c to recognize them as being in the Winsock
!  * error code range and pass them off to win32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
*************** int            pgwin32_connect(SOCKET s, const st
*** 456,463 ****
  int            pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval
*timeout);
  int            pgwin32_recv(SOCKET s, char *buf, int len, int flags);
  int            pgwin32_send(SOCKET s, const void *buf, int len, int flags);
-
- const char *pgwin32_socket_strerror(int err);
  int            pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);

  extern int    pgwin32_noblock;
--- 456,461 ----
diff --git a/src/interfaces/ecpg/compatlib/.gitignore b/src/interfaces/ecpg/compatlib/.gitignore
index ad5ba13..8b9aa95 100644
*** a/src/interfaces/ecpg/compatlib/.gitignore
--- b/src/interfaces/ecpg/compatlib/.gitignore
***************
*** 2,5 ****
--- 2,6 ----
  /blibecpg_compatdll.def
  /exports.list
  /snprintf.c
+ /strerror.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index ebfd895..b7bd162 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,37 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

*************** submake-pgtypeslib:
*** 48,54 ****
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
--- 48,54 ----
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strerror.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
index 1619e97..545c106 100644
*** a/src/interfaces/ecpg/ecpglib/.gitignore
--- b/src/interfaces/ecpg/ecpglib/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /path.c
  /pgstrcasecmp.c
  /snprintf.c
+ /strerror.c
  /strlcpy.c
  /strnlen.c
  /thread.c
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index d25d248..005d25a 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,32 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

--- 26,32 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

*************** include $(top_srcdir)/src/Makefile.shlib
*** 57,63 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
--- 57,63 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % :
$(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
*************** uninstall: uninstall-lib
*** 74,79 ****

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
--- 74,79 ----

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/pgtypeslib/.gitignore b/src/interfaces/ecpg/pgtypeslib/.gitignore
index d5f0fae..b3fae08 100644
*** a/src/interfaces/ecpg/pgtypeslib/.gitignore
--- b/src/interfaces/ecpg/pgtypeslib/.gitignore
***************
*** 4,8 ****
--- 4,9 ----
  /pgstrcasecmp.c
  /rint.c
  /snprintf.c
+ /strerror.c
  /string.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 29264ce..18b2402 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,36 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
--- 30,36 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
*************** include $(top_srcdir)/src/Makefile.shlib
*** 45,51 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
--- 45,51 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index abe0a50..9af7cc0 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strerror.o strlcpy.o
strnlen.owin32error.o win32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 5c2e6a8..6cc323a 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 27,33 ****
   */
  #undef _POSIX_C_SOURCE
  #undef _XOPEN_SOURCE
- #undef HAVE_STRERROR
  #undef HAVE_TZNAME

  /*
--- 27,32 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index d7467fb..b3a10ba 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,39 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/strerror.c b/src/port/strerror.c
index e92ebc9..e3393eb 100644
*** a/src/port/strerror.c
--- b/src/port/strerror.c
***************
*** 1,30 ****
! /* src/port/strerror.c */
!
! /*
!  * strerror - map error number to descriptive string
   *
!  * This version is obviously somewhat Unix-specific.
   *
!  * based on code by Henry Spencer
!  * modified for ANSI by D'Arcy J.M. Cain
   */
-
  #include "c.h"


! extern const char *const sys_errlist[];
! extern int    sys_nerr;

! const char *
! strerror(int errnum)
  {
!     static char buf[24];

!     if (errnum < 0 || errnum > sys_nerr)
      {
!         sprintf(buf, _("unrecognized error %d"), errnum);
!         return buf;
      }

!     return sys_errlist[errnum];
  }
--- 1,285 ----
! /*-------------------------------------------------------------------------
   *
!  * strerror.c
!  *      Replacement for standard strerror() function
   *
!  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
!  *
!  *
!  * IDENTIFICATION
!  *      src/port/strerror.c
!  *
!  *-------------------------------------------------------------------------
   */
  #include "c.h"

+ /*
+  * Within this file, "strerror" means the platform's function not pg_strerror
+  */
+ #undef strerror

! static char *get_errno_symbol(int errnum);
! #ifdef WIN32
! static char *win32_socket_strerror(int errnum);
! #endif

!
! /*
!  * A slightly cleaned-up version of strerror()
!  */
! char *
! pg_strerror(int errnum)
  {
!     /* this buffer is only used if strerror() and get_errno_symbol() fail */
!     static char errorstr_buf[48];
!     char       *str;

!     /* If it's a Windows Winsock error, that needs special handling */
! #ifdef WIN32
!     /* Winsock error code range, per WinError.h */
!     if (errnum >= 10000 && errnum <= 11999)
!         return win32_socket_strerror(errnum);
! #endif
!
!     /* Try the platform's strerror() */
!     str = strerror(errnum);
!
!     /*
!      * Some strerror()s return an empty string for out-of-range errno.  This
!      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
!      * back strings of question marks if libc cannot transcode the message to
!      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
!      * get_errno_symbol(), and if that fails, print the numeric errno.
!      */
!     if (str == NULL || *str == '\0' || *str == '?')
!         str = get_errno_symbol(errnum);
!
!     if (str == NULL)
      {
!         snprintf(errorstr_buf, sizeof(errorstr_buf),
!         /*------
!           translator: This string will be truncated at 47
!           characters expanded. */
!                  _("operating system error %d"), errnum);
!         str = errorstr_buf;
      }

!     return str;
  }
+
+ /*
+  * Returns a symbol (e.g. "ENOENT") for an errno code.
+  * Returns NULL if the code is unrecognized.
+  */
+ static char *
+ get_errno_symbol(int errnum)
+ {
+     switch (errnum)
+     {
+         case E2BIG:
+             return "E2BIG";
+         case EACCES:
+             return "EACCES";
+ #ifdef EADDRINUSE
+         case EADDRINUSE:
+             return "EADDRINUSE";
+ #endif
+ #ifdef EADDRNOTAVAIL
+         case EADDRNOTAVAIL:
+             return "EADDRNOTAVAIL";
+ #endif
+         case EAFNOSUPPORT:
+             return "EAFNOSUPPORT";
+ #ifdef EAGAIN
+         case EAGAIN:
+             return "EAGAIN";
+ #endif
+ #ifdef EALREADY
+         case EALREADY:
+             return "EALREADY";
+ #endif
+         case EBADF:
+             return "EBADF";
+ #ifdef EBADMSG
+         case EBADMSG:
+             return "EBADMSG";
+ #endif
+         case EBUSY:
+             return "EBUSY";
+         case ECHILD:
+             return "ECHILD";
+ #ifdef ECONNABORTED
+         case ECONNABORTED:
+             return "ECONNABORTED";
+ #endif
+         case ECONNREFUSED:
+             return "ECONNREFUSED";
+ #ifdef ECONNRESET
+         case ECONNRESET:
+             return "ECONNRESET";
+ #endif
+         case EDEADLK:
+             return "EDEADLK";
+         case EDOM:
+             return "EDOM";
+         case EEXIST:
+             return "EEXIST";
+         case EFAULT:
+             return "EFAULT";
+         case EFBIG:
+             return "EFBIG";
+ #ifdef EHOSTUNREACH
+         case EHOSTUNREACH:
+             return "EHOSTUNREACH";
+ #endif
+         case EIDRM:
+             return "EIDRM";
+         case EINPROGRESS:
+             return "EINPROGRESS";
+         case EINTR:
+             return "EINTR";
+         case EINVAL:
+             return "EINVAL";
+         case EIO:
+             return "EIO";
+ #ifdef EISCONN
+         case EISCONN:
+             return "EISCONN";
+ #endif
+         case EISDIR:
+             return "EISDIR";
+ #ifdef ELOOP
+         case ELOOP:
+             return "ELOOP";
+ #endif
+         case EMFILE:
+             return "EMFILE";
+         case EMLINK:
+             return "EMLINK";
+         case EMSGSIZE:
+             return "EMSGSIZE";
+         case ENAMETOOLONG:
+             return "ENAMETOOLONG";
+         case ENFILE:
+             return "ENFILE";
+         case ENOBUFS:
+             return "ENOBUFS";
+         case ENODEV:
+             return "ENODEV";
+         case ENOENT:
+             return "ENOENT";
+         case ENOEXEC:
+             return "ENOEXEC";
+         case ENOMEM:
+             return "ENOMEM";
+         case ENOSPC:
+             return "ENOSPC";
+         case ENOSYS:
+             return "ENOSYS";
+ #ifdef ENOTCONN
+         case ENOTCONN:
+             return "ENOTCONN";
+ #endif
+         case ENOTDIR:
+             return "ENOTDIR";
+ #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
+         case ENOTEMPTY:
+             return "ENOTEMPTY";
+ #endif
+ #ifdef ENOTSOCK
+         case ENOTSOCK:
+             return "ENOTSOCK";
+ #endif
+ #ifdef ENOTSUP
+         case ENOTSUP:
+             return "ENOTSUP";
+ #endif
+         case ENOTTY:
+             return "ENOTTY";
+         case ENXIO:
+             return "ENXIO";
+ #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
+         case EOPNOTSUPP:
+             return "EOPNOTSUPP";
+ #endif
+ #ifdef EOVERFLOW
+         case EOVERFLOW:
+             return "EOVERFLOW";
+ #endif
+         case EPERM:
+             return "EPERM";
+         case EPIPE:
+             return "EPIPE";
+         case EPROTONOSUPPORT:
+             return "EPROTONOSUPPORT";
+         case ERANGE:
+             return "ERANGE";
+ #ifdef EROFS
+         case EROFS:
+             return "EROFS";
+ #endif
+         case ESRCH:
+             return "ESRCH";
+ #ifdef ETIMEDOUT
+         case ETIMEDOUT:
+             return "ETIMEDOUT";
+ #endif
+ #ifdef ETXTBSY
+         case ETXTBSY:
+             return "ETXTBSY";
+ #endif
+ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+         case EWOULDBLOCK:
+             return "EWOULDBLOCK";
+ #endif
+         case EXDEV:
+             return "EXDEV";
+     }
+
+     return NULL;
+ }
+
+
+ #ifdef WIN32
+
+ /*
+  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
+  */
+ static char *
+ win32_socket_strerror(int errnum)
+ {
+     static char wserrbuf[256];
+     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
+
+     if (handleDLL == INVALID_HANDLE_VALUE)
+     {
+         handleDLL = LoadLibraryEx("netmsg.dll", NULL,
+                                   DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
+         if (handleDLL == NULL)
+         {
+             /* just treat this as an unrecognized error ... */
+             sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+             return wserrbuf;
+         }
+     }
+
+     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
+     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_FROM_HMODULE,
+                       handleDLL,
+                       errnum,
+                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+                       wserrbuf,
+                       sizeof(wserrbuf) - 1,
+                       NULL) == 0)
+     {
+         /* Failed to get id */
+         sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+     }
+
+     return wserrbuf;
+ }
+
+ #endif                            /* WIN32 */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4543d87..24e9ddf 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 98,104 ****
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
--- 98,104 ----
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c strerror.c tar.c thread.c getopt.c getopt_long.c dirent.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 67675a3..80e4f8c 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 20,34 ****
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", which matches the features glibc supports, notably
! # %m, 'z' and 'll' width modifiers ('ll' only matters if int64 requires it),
! # and argument order control if we're doing --enable-nls.  On platforms where
! # the native printf doesn't have 'z'/'ll' or arg control, we replace it with
! # src/port/snprintf.c which does, so that the only potential mismatch here is
! # whether or not %m is supported.  We need that for elog/ereport, so we live
! # with the fact that erroneous use of %m in plain printf calls won't be
! # detected.  (It appears that many versions of gcc/clang wouldn't report it
! # even if told to check according to plain printf archetype, anyway.)
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
--- 20,27 ----
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
diff --git a/config/c-library.m4 b/config/c-library.m4
index da7fa77..d371f1b 100644
*** a/config/c-library.m4
--- b/config/c-library.m4
*************** AC_DEFUN([PGAC_STRUCT_ADDRINFO],
*** 171,276 ****
  ])])# PGAC_STRUCT_ADDRINFO


- # PGAC_FUNC_SNPRINTF_ARG_CONTROL
- # ---------------------------------------
- # Determine if snprintf supports %1$ argument selection, e.g. %5$ selects
- # the fifth argument after the printf format string.
- # This is not in the C99 standard, but in the Single Unix Specification (SUS).
- # It is used in our language translation strings.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_ARG_CONTROL],
- [AC_MSG_CHECKING([whether snprintf supports argument control])
- AC_CACHE_VAL(pgac_cv_snprintf_arg_control,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_arg_control=yes],
- [pgac_cv_snprintf_arg_control=no],
- [pgac_cv_snprintf_arg_control=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_arg_control])
- ])# PGAC_FUNC_SNPRINTF_ARG_CONTROL
-
- # PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
- # ---------------------------------
- # Determine if snprintf supports the z length modifier for printing
- # size_t-sized variables. That's supported by C99 and POSIX but not
- # all platforms play ball, so we must test whether it's working.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT],
- [AC_MSG_CHECKING([whether snprintf supports the %z modifier])
- AC_CACHE_VAL(pgac_cv_snprintf_size_t_support,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_size_t_support=yes],
- [pgac_cv_snprintf_size_t_support=no],
- [pgac_cv_snprintf_size_t_support=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_size_t_support])
- ])# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-
- # PGAC_FUNC_SNPRINTF_C99_RESULT
- # -----------------------------
- # Determine whether snprintf returns the desired buffer length when
- # it overruns the actual buffer length.  That's required by C99 and POSIX
- # but ancient platforms don't behave that way, so we must test.
- # While we're at it, let's just verify that it doesn't physically overrun
- # the buffer.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_C99_RESULT],
- [AC_MSG_CHECKING([whether snprintf handles buffer overrun per C99])
- AC_CACHE_VAL(pgac_cv_snprintf_c99_result,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_c99_result=yes],
- [pgac_cv_snprintf_c99_result=no],
- [pgac_cv_snprintf_c99_result=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_c99_result])
- ])# PGAC_FUNC_SNPRINTF_C99_RESULT
-
-
  # PGAC_TYPE_LOCALE_T
  # ------------------
  # Check for the locale_t type and find the right header file.  macOS
--- 171,176 ----
diff --git a/configure b/configure
index fadd06e..58f223f 100755
*** a/configure
--- b/configure
*************** $as_echo "#define HAVE_PS_STRINGS 1" >>c
*** 15273,15369 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   for ac_func in snprintf
- do :
-   ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
- if test "x$ac_cv_func_snprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_SNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
-   for ac_func in vsnprintf
- do :
-   ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
- if test "x$ac_cv_func_vsnprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_VSNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- ac_fn_c_check_decl "$LINENO" "snprintf" "ac_cv_have_decl_snprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_snprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_SNPRINTF $ac_have_decl
- _ACEOF
- ac_fn_c_check_decl "$LINENO" "vsnprintf" "ac_cv_have_decl_vsnprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_vsnprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_VSNPRINTF $ac_have_decl
- _ACEOF
-
-
-
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for isinf" >&5
  $as_echo_n "checking for isinf... " >&6; }
  if ${ac_cv_func_isinf+:} false; then :
--- 15273,15278 ----
*************** fi
*** 16068,16120 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports argument control" >&5
- $as_echo_n "checking whether snprintf supports argument control... " >&6; }
- if ${pgac_cv_snprintf_arg_control+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_arg_control=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_arg_control=yes
- else
-   pgac_cv_snprintf_arg_control=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_arg_control" >&5
- $as_echo "$pgac_cv_snprintf_arg_control" >&6; }
-
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-

  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long int is 64 bits" >&5
  $as_echo_n "checking whether long int is 64 bits... " >&6; }
--- 15977,15982 ----
*************** _ACEOF
*** 16294,16301 ****


  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 16156,16161 ----
*************** cat >>confdefs.h <<_ACEOF
*** 16308,16427 ****
  _ACEOF


- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports the %z modifier" >&5
- $as_echo_n "checking whether snprintf supports the %z modifier... " >&6; }
- if ${pgac_cv_snprintf_size_t_support+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_size_t_support=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_size_t_support=yes
- else
-   pgac_cv_snprintf_size_t_support=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_size_t_support" >&5
- $as_echo "$pgac_cv_snprintf_size_t_support" >&6; }
-
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf handles buffer overrun per C99" >&5
- $as_echo_n "checking whether snprintf handles buffer overrun per C99... " >&6; }
- if ${pgac_cv_snprintf_c99_result+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_c99_result=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_c99_result=yes
- else
-   pgac_cv_snprintf_c99_result=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_c99_result" >&5
- $as_echo "$pgac_cv_snprintf_c99_result" >&6; }
-
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-
- $as_echo "#define USE_REPL_SNPRINTF 1" >>confdefs.h
-
-   case " $LIBOBJS " in
-   *" snprintf.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
-  ;;
- esac
-
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
--- 16168,16173 ----
diff --git a/configure.in b/configure.in
index 3adec10..aca5503 100644
*** a/configure.in
--- b/configure.in
*************** if test "$pgac_cv_var_PS_STRINGS" = yes
*** 1584,1636 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   AC_CHECK_FUNCS(snprintf, [], pgac_need_repl_snprintf=yes)
-   AC_CHECK_FUNCS(vsnprintf, [], pgac_need_repl_snprintf=yes)
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- AC_CHECK_DECLS([snprintf, vsnprintf])
-
-
  dnl Cannot use AC_CHECK_FUNC because isinf may be a macro
  AC_CACHE_CHECK([for isinf], ac_cv_func_isinf,
  [AC_LINK_IFELSE([AC_LANG_PROGRAM([
--- 1584,1589 ----
*************** for the exact reason.]])],
*** 1800,1815 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_ARG_CONTROL
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-
  dnl Check to see if we have a working 64-bit integer type.
  dnl Since Postgres 8.4, we no longer support compilers without a working
  dnl 64-bit type; but we have to determine whether that type is called
--- 1753,1758 ----
*************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in
*** 1832,1839 ****
    [Define to the name of a signed 64-bit integer type.])

  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 1775,1780 ----
*************** fi
*** 1843,1872 ****
  AC_DEFINE_UNQUOTED(INT64_MODIFIER, $INT64_MODIFIER,
                     [Define to the appropriate printf length modifier for 64-bit ints.])

- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_C99_RESULT
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-   AC_DEFINE(USE_REPL_SNPRINTF, 1, [Use replacement snprintf() functions.])
-   AC_LIBOBJ(snprintf)
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  PGAC_C_BUILTIN_OP_OVERFLOW
--- 1784,1789 ----
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 798a823..df7e01f 100644
*** a/src/backend/lib/stringinfo.c
--- b/src/backend/lib/stringinfo.c
*************** resetStringInfo(StringInfo str)
*** 77,88 ****
--- 77,91 ----
  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);
*************** appendStringInfo(StringInfo str, const c
*** 105,110 ****
--- 108,116 ----
   * 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 22e5d87..b9c1130 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void write_csvlog(ErrorData *edat
*** 177,183 ****
  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);
--- 177,182 ----
*************** errcode_for_socket_access(void)
*** 705,717 ****
   */
  #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); \
--- 704,713 ----
*************** errcode_for_socket_access(void)
*** 722,736 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 718,731 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** errcode_for_socket_access(void)
*** 746,760 ****
  #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); \
--- 741,752 ----
*************** errcode_for_socket_access(void)
*** 765,779 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 757,770 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** send_message_to_frontend(ErrorData *edat
*** 3329,3387 ****


  /*
-  * 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
--- 3320,3325 ----
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 45a391b..7f3b4ba 100644
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** archputs(const char *s, Archive *AH)
*** 1471,1476 ****
--- 1471,1477 ----
  int
  archprintf(Archive *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** archprintf(Archive *AH, const char *fmt,
*** 1483,1488 ****
--- 1484,1490 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
*************** RestoreOutput(ArchiveHandle *AH, OutputC
*** 1604,1609 ****
--- 1606,1612 ----
  int
  ahprintf(ArchiveHandle *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** ahprintf(ArchiveHandle *AH, const char *
*** 1616,1621 ****
--- 1619,1625 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 007be12..407a56d 100644
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
*************** _EndBlobs(ArchiveHandle *AH, TocEntry *t
*** 1026,1031 ****
--- 1026,1032 ----
  static int
  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** tarPrintf(ArchiveHandle *AH, TAR_MEMBER
*** 1038,1043 ****
--- 1039,1045 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 04465a1..2cf100f 100644
*** a/src/common/psprintf.c
--- b/src/common/psprintf.c
***************
*** 45,50 ****
--- 45,51 ----
  char *
  psprintf(const char *fmt,...)
  {
+     int            save_errno = errno;
      size_t        len = 128;        /* initial assumption about buffer size */

      for (;;)
*************** psprintf(const char *fmt,...)
*** 60,65 ****
--- 61,67 ----
          result = (char *) palloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          newlen = pvsnprintf(result, len, fmt, args);
          va_end(args);
*************** psprintf(const char *fmt,...)
*** 89,94 ****
--- 91,99 ----
   * Other error cases do not return, but exit via elog(ERROR) or exit().
   * Hence, this shouldn't be used inside libpq.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * 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.
diff --git a/src/include/c.h b/src/include/c.h
index 1e50103..6a8a01c 100644
*** a/src/include/c.h
--- b/src/include/c.h
*************** extern void ExceptionalCondition(const c
*** 1088,1101 ****
   * standard C library.
   */

- #if !HAVE_DECL_SNPRINTF
- extern int    snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
- #endif
-
- #if !HAVE_DECL_VSNPRINTF
- extern int    vsnprintf(char *str, size_t count, const char *fmt, va_list args);
- #endif
-
  #if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC
  extern int    fdatasync(int fildes);
  #endif
--- 1088,1093 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 2861b1f..16c148b 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 154,163 ****
     don't. */
  #undef HAVE_DECL_POSIX_FADVISE

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_SNPRINTF
-
  /* Define to 1 if you have the declaration of `strlcat', and to 0 if you
     don't. */
  #undef HAVE_DECL_STRLCAT
--- 154,159 ----
***************
*** 182,191 ****
     don't. */
  #undef HAVE_DECL_SYS_SIGLIST

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_VSNPRINTF
-
  /* Define to 1 if you have the `dlopen' function. */
  #undef HAVE_DLOPEN

--- 178,183 ----
***************
*** 495,503 ****
  /* Define to 1 if you have the `shm_open' function. */
  #undef HAVE_SHM_OPEN

- /* Define to 1 if you have the `snprintf' function. */
- #undef HAVE_SNPRINTF
-
  /* Define to 1 if you have spinlocks. */
  #undef HAVE_SPINLOCKS

--- 487,492 ----
***************
*** 700,708 ****
  /* Define to 1 if you have the <uuid/uuid.h> header file. */
  #undef HAVE_UUID_UUID_H

- /* Define to 1 if you have the `vsnprintf' function. */
- #undef HAVE_VSNPRINTF
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #undef HAVE_WCHAR_H

--- 689,694 ----
***************
*** 914,922 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  #undef USE_PAM

- /* Use replacement snprintf() functions. */
- #undef USE_REPL_SNPRINTF
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #undef USE_SLICING_BY_8_CRC32C

--- 900,905 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 4c68fa8..e080270 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 123,132 ****
     to 0 if you don't. */
  #define HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN 0

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_SNPRINTF 1
-
  /* Define to 1 if you have the declaration of `strnlen', and to 0 if you
     don't. */
  #define HAVE_DECL_STRNLEN 1
--- 123,128 ----
***************
*** 139,148 ****
     don't. */
  #define HAVE_DECL_STRTOULL 1

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_VSNPRINTF 1
-
  /* Define to 1 if you have the `dlopen' function. */
  /* #undef HAVE_DLOPEN */

--- 135,140 ----
***************
*** 361,369 ****
  /* Define to 1 if you have the `setsid' function. */
  /* #undef HAVE_SETSID */

- /* Define to 1 if you have the `snprintf' function. */
- /* #undef HAVE_SNPRINTF */
-
  /* Define to 1 if you have spinlocks. */
  #define HAVE_SPINLOCKS 1

--- 353,358 ----
***************
*** 541,549 ****
  /* Define to 1 if you have the <utime.h> header file. */
  #define HAVE_UTIME_H 1

- /* Define to 1 if you have the `vsnprintf' function. */
- #define HAVE_VSNPRINTF 1
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #define HAVE_WCHAR_H 1

--- 530,535 ----
***************
*** 703,711 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  /* #undef USE_PAM */

- /* Use replacement snprintf() functions. */
- #define USE_REPL_SNPRINTF 1
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #if (_MSC_VER < 1500)
  #define USE_SLICING_BY_8_CRC32C 1
--- 689,694 ----
diff --git a/src/include/port.h b/src/include/port.h
index dae80cb..3a186b4 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern unsigned char pg_tolower(unsigned
*** 134,140 ****
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! #ifdef USE_REPL_SNPRINTF

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
--- 134,145 ----
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! /*
!  * Beginning in v12, we always replace snprintf() and friends with our own
!  * implementation.  This symbol is no longer consulted by the core code,
!  * but keep it defined anyway in case any extensions are looking at it.
!  */
! #define USE_REPL_SNPRINTF 1

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
*************** extern int    pg_printf(const char *fmt,...
*** 187,193 ****
  #define fprintf            pg_fprintf
  #define printf            pg_printf
  #endif
- #endif                            /* USE_REPL_SNPRINTF */

  /* Replace strerror() with our own, somewhat more robust wrapper */
  extern char *pg_strerror(int errnum);
--- 192,197 ----
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index b7bd162..e07a7fa 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,38 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o snprintf.o strerror.o \
!     $(filter strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index 005d25a..b381623 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,33 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
!     $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
--- 26,33 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 18b2402..15d7f33 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,37 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
!     $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

--- 30,37 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter rint.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 9af7cc0..a694560 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     snprintf.o strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o strlcpy.o strnlen.o win32error.o
win32setlocale.o,$(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 0814ec6..43c36c3 100644
*** a/src/interfaces/libpq/pqexpbuffer.c
--- b/src/interfaces/libpq/pqexpbuffer.c
*************** enlargePQExpBuffer(PQExpBuffer str, size
*** 233,238 ****
--- 233,239 ----
  void
  printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** printfPQExpBuffer(PQExpBuffer str, const
*** 244,249 ****
--- 245,251 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 261,266 ****
--- 263,269 ----
  void
  appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** appendPQExpBuffer(PQExpBuffer str, const
*** 270,275 ****
--- 273,279 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 281,286 ****
--- 285,293 ----
   * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
   * Attempt to format data and append it to str.  Returns true if done
   * (either successful or hard failure), false if need to retry.
+  *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
   */
  static bool
  appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index e6241f0..f8888a4 100644
*** a/src/pl/plperl/plperl.h
--- b/src/pl/plperl/plperl.h
***************
*** 29,39 ****
   * Sometimes perl carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  /*
   * ActivePerl 5.18 and later are MinGW-built, and their headers use GCC's
--- 29,36 ----
***************
*** 99,105 ****
  #endif

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 96,101 ----
***************
*** 113,119 ****
  #define vsnprintf        pg_vsnprintf
  #define snprintf        pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /* perl version and platform portability */
  #define NEED_eval_pv
--- 109,114 ----
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index e244104..3814a6c 100644
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** static bool set_string_attr(PyObject *ob
*** 46,51 ****
--- 46,52 ----
  void
  PLy_elog_impl(int elevel, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *xmsg;
      char       *tbmsg;
      int            tb_depth;
*************** PLy_elog_impl(int elevel, const char *fm
*** 96,101 ****
--- 97,103 ----
              va_list        ap;
              int            needed;

+             errno = save_errno;
              va_start(ap, fmt);
              needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
              va_end(ap);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 6cc323a..aefbfc2 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 33,43 ****
   * Sometimes python carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  #if defined(_MSC_VER) && defined(_DEBUG)
  /* Python uses #pragma to bring in a non-default libpython on VC++ if
--- 33,40 ----
*************** typedef int Py_ssize_t;
*** 124,130 ****
  #include <eval.h>

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 121,126 ----
*************** typedef int Py_ssize_t;
*** 138,144 ****
  #define vsnprintf                pg_vsnprintf
  #define snprintf                pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /*
   * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
--- 134,139 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index b3a10ba..a2ee8e2 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,40 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o snprintf.o sprompt.o strerror.o \
!     tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/README b/src/port/README
index 4ae96da..c446b46 100644
*** a/src/port/README
--- b/src/port/README
*************** and adding infrastructure to recompile t
*** 18,24 ****

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter snprintf.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
--- 18,24 ----

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter strlcat.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 851e2ae..63cec5d 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 64,69 ****
--- 64,77 ----
   *
   * 5. Space and '#' flags are not implemented.
   *
+  * In addition, we support some extensions over C99:
+  *
+  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
+  *
+  * 2. "%m" expands to the value of strerror(errno), where errno is the
+  * value that variable had at the start of the call.  This is a glibc
+  * extension, but a very useful one.
+  *
   *
   * Historically the result values of sprintf/snprintf varied across platforms.
   * This implementation now follows the C99 standard:
*************** static void flushbuffer(PrintfTarget *ta
*** 155,160 ****
--- 163,175 ----
  static void dopr(PrintfTarget *target, const char *format, va_list args);


+ /*
+  * Externally visible entry points.
+  *
+  * All of these are just wrappers around dopr().  Note it's essential that
+  * they not change the value of "errno" before reaching dopr().
+  */
+
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** static void trailing_pad(int *padlen, Pr
*** 315,325 ****


  /*
!  * dopr(): poor man's version of doprintf
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
--- 330,341 ----


  /*
!  * dopr(): the guts of *printf for all cases.
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
+     int            save_errno = errno;
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
*************** nextch1:
*** 497,502 ****
--- 513,519 ----
                  else
                      have_non_dollar = true;
                  break;
+             case 'm':
              case '%':
                  break;
          }
*************** nextch2:
*** 802,807 ****
--- 819,831 ----
                           precision, pointflag,
                           target);
                  break;
+             case 'm':
+                 {
+                     const char *errm = strerror(save_errno);
+
+                     dostr(errm, strlen(errm), target);
+                 }
+                 break;
              case '%':
                  dopr_outch('%', target);
                  break;

Re: Allowing printf("%m") only where it actually works

От
Michael Paquier
Дата:
On Sun, Aug 19, 2018 at 03:12:00PM -0400, Tom Lane wrote:
> * The Windows aspects of this are untested.  It seems like importing
> pgwin32_socket_strerror's behavior into the frontend ought to be a
> bug fix, though: win32_port.h redefines socket error symbols whether
> FRONTEND is set or not, so aren't we printing bogus info for socket
> errors in frontend right now?

I had a look at that this morning for some other Windows patch, and I
think that HEAD is flat wrong to not expose pgwin32_socket_strerror to
the frontend.

I would have liked to look at this patch in details, but it failed to
apply.  Could you rebase?
--
Michael

Вложения

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Michael Paquier <michael@paquier.xyz> writes:
> I would have liked to look at this patch in details, but it failed to
> apply.  Could you rebase?

Ah, yeah, the dlopen patch touched a couple of the same places.
Rebase attached --- no substantive changes.

            regards, tom lane

diff --git a/configure b/configure
index dd77742..1aefc57 100755
*** a/configure
--- b/configure
*************** esac
*** 15602,15620 ****

  fi

- ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
- if test "x$ac_cv_func_strerror" = xyes; then :
-   $as_echo "#define HAVE_STRERROR 1" >>confdefs.h
-
- else
-   case " $LIBOBJS " in
-   *" strerror.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS strerror.$ac_objext"
-  ;;
- esac
-
- fi
-
  ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
  if test "x$ac_cv_func_strlcat" = xyes; then :
    $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
--- 15602,15607 ----
diff --git a/configure.in b/configure.in
index 3ada48b..3a23913 100644
*** a/configure.in
--- b/configure.in
*************** else
*** 1660,1666 ****
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strerror strlcat strlcpy
strnlen])

  case $host_os in

--- 1660,1666 ----
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strlcat strlcpy strnlen])

  case $host_os in

diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index f4356fe..af35cfb 100644
*** a/src/backend/port/win32/socket.c
--- b/src/backend/port/win32/socket.c
*************** pgwin32_select(int nfds, fd_set *readfds
*** 690,728 ****
          memcpy(writefds, &outwritefds, sizeof(fd_set));
      return nummatches;
  }
-
-
- /*
-  * Return win32 error string, since strerror can't
-  * handle winsock codes
-  */
- static char wserrbuf[256];
- const char *
- pgwin32_socket_strerror(int err)
- {
-     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
-
-     if (handleDLL == INVALID_HANDLE_VALUE)
-     {
-         handleDLL = LoadLibraryEx("netmsg.dll", NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
-         if (handleDLL == NULL)
-             ereport(FATAL,
-                     (errmsg_internal("could not load netmsg.dll: error code %lu", GetLastError())));
-     }
-
-     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
-     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
-                       FORMAT_MESSAGE_FROM_SYSTEM |
-                       FORMAT_MESSAGE_FROM_HMODULE,
-                       handleDLL,
-                       err,
-                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
-                       wserrbuf,
-                       sizeof(wserrbuf) - 1,
-                       NULL) == 0)
-     {
-         /* Failed to get id */
-         sprintf(wserrbuf, "unrecognized winsock error %d", err);
-     }
-     return wserrbuf;
- }
--- 690,692 ----
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 16531f7..22e5d87 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void send_message_to_server_log(E
*** 178,185 ****
  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 *useful_strerror(int errnum);
- static const char *get_errno_symbol(int errnum);
  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);
--- 178,183 ----
*************** expand_fmt_string(const char *fmt, Error
*** 3360,3366 ****
                   */
                  const char *cp2;

!                 cp2 = useful_strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
--- 3358,3364 ----
                   */
                  const char *cp2;

!                 cp2 = strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
*************** expand_fmt_string(const char *fmt, Error
*** 3384,3602 ****


  /*
-  * A slightly cleaned-up version of strerror()
-  */
- static const char *
- useful_strerror(int errnum)
- {
-     /* this buffer is only used if strerror() and get_errno_symbol() fail */
-     static char errorstr_buf[48];
-     const char *str;
-
- #ifdef WIN32
-     /* Winsock error code range, per WinError.h */
-     if (errnum >= 10000 && errnum <= 11999)
-         return pgwin32_socket_strerror(errnum);
- #endif
-     str = strerror(errnum);
-
-     /*
-      * Some strerror()s return an empty string for out-of-range errno.  This
-      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
-      * back strings of question marks if libc cannot transcode the message to
-      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
-      * get_errno_symbol(), and if that fails, print the numeric errno.
-      */
-     if (str == NULL || *str == '\0' || *str == '?')
-         str = get_errno_symbol(errnum);
-
-     if (str == NULL)
-     {
-         snprintf(errorstr_buf, sizeof(errorstr_buf),
-         /*------
-           translator: This string will be truncated at 47
-           characters expanded. */
-                  _("operating system error %d"), errnum);
-         str = errorstr_buf;
-     }
-
-     return str;
- }
-
- /*
-  * Returns a symbol (e.g. "ENOENT") for an errno code.
-  * Returns NULL if the code is unrecognized.
-  */
- static const char *
- get_errno_symbol(int errnum)
- {
-     switch (errnum)
-     {
-         case E2BIG:
-             return "E2BIG";
-         case EACCES:
-             return "EACCES";
- #ifdef EADDRINUSE
-         case EADDRINUSE:
-             return "EADDRINUSE";
- #endif
- #ifdef EADDRNOTAVAIL
-         case EADDRNOTAVAIL:
-             return "EADDRNOTAVAIL";
- #endif
-         case EAFNOSUPPORT:
-             return "EAFNOSUPPORT";
- #ifdef EAGAIN
-         case EAGAIN:
-             return "EAGAIN";
- #endif
- #ifdef EALREADY
-         case EALREADY:
-             return "EALREADY";
- #endif
-         case EBADF:
-             return "EBADF";
- #ifdef EBADMSG
-         case EBADMSG:
-             return "EBADMSG";
- #endif
-         case EBUSY:
-             return "EBUSY";
-         case ECHILD:
-             return "ECHILD";
- #ifdef ECONNABORTED
-         case ECONNABORTED:
-             return "ECONNABORTED";
- #endif
-         case ECONNREFUSED:
-             return "ECONNREFUSED";
- #ifdef ECONNRESET
-         case ECONNRESET:
-             return "ECONNRESET";
- #endif
-         case EDEADLK:
-             return "EDEADLK";
-         case EDOM:
-             return "EDOM";
-         case EEXIST:
-             return "EEXIST";
-         case EFAULT:
-             return "EFAULT";
-         case EFBIG:
-             return "EFBIG";
- #ifdef EHOSTUNREACH
-         case EHOSTUNREACH:
-             return "EHOSTUNREACH";
- #endif
-         case EIDRM:
-             return "EIDRM";
-         case EINPROGRESS:
-             return "EINPROGRESS";
-         case EINTR:
-             return "EINTR";
-         case EINVAL:
-             return "EINVAL";
-         case EIO:
-             return "EIO";
- #ifdef EISCONN
-         case EISCONN:
-             return "EISCONN";
- #endif
-         case EISDIR:
-             return "EISDIR";
- #ifdef ELOOP
-         case ELOOP:
-             return "ELOOP";
- #endif
-         case EMFILE:
-             return "EMFILE";
-         case EMLINK:
-             return "EMLINK";
-         case EMSGSIZE:
-             return "EMSGSIZE";
-         case ENAMETOOLONG:
-             return "ENAMETOOLONG";
-         case ENFILE:
-             return "ENFILE";
-         case ENOBUFS:
-             return "ENOBUFS";
-         case ENODEV:
-             return "ENODEV";
-         case ENOENT:
-             return "ENOENT";
-         case ENOEXEC:
-             return "ENOEXEC";
-         case ENOMEM:
-             return "ENOMEM";
-         case ENOSPC:
-             return "ENOSPC";
-         case ENOSYS:
-             return "ENOSYS";
- #ifdef ENOTCONN
-         case ENOTCONN:
-             return "ENOTCONN";
- #endif
-         case ENOTDIR:
-             return "ENOTDIR";
- #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
-         case ENOTEMPTY:
-             return "ENOTEMPTY";
- #endif
- #ifdef ENOTSOCK
-         case ENOTSOCK:
-             return "ENOTSOCK";
- #endif
- #ifdef ENOTSUP
-         case ENOTSUP:
-             return "ENOTSUP";
- #endif
-         case ENOTTY:
-             return "ENOTTY";
-         case ENXIO:
-             return "ENXIO";
- #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
-         case EOPNOTSUPP:
-             return "EOPNOTSUPP";
- #endif
- #ifdef EOVERFLOW
-         case EOVERFLOW:
-             return "EOVERFLOW";
- #endif
-         case EPERM:
-             return "EPERM";
-         case EPIPE:
-             return "EPIPE";
-         case EPROTONOSUPPORT:
-             return "EPROTONOSUPPORT";
-         case ERANGE:
-             return "ERANGE";
- #ifdef EROFS
-         case EROFS:
-             return "EROFS";
- #endif
-         case ESRCH:
-             return "ESRCH";
- #ifdef ETIMEDOUT
-         case ETIMEDOUT:
-             return "ETIMEDOUT";
- #endif
- #ifdef ETXTBSY
-         case ETXTBSY:
-             return "ETXTBSY";
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-         case EWOULDBLOCK:
-             return "EWOULDBLOCK";
- #endif
-         case EXDEV:
-             return "EXDEV";
-     }
-
-     return NULL;
- }
-
-
- /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
--- 3382,3387 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4094e22..705c52b 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 531,539 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #undef HAVE_STDLIB_H

- /* Define to 1 if you have the `strerror' function. */
- #undef HAVE_STRERROR
-
  /* Define to 1 if you have the `strerror_r' function. */
  #undef HAVE_STRERROR_R

--- 531,536 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 6618b43..eafe377 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 402,412 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #define HAVE_STDLIB_H 1

- /* Define to 1 if you have the `strerror' function. */
- #ifndef HAVE_STRERROR
- #define HAVE_STRERROR 1
- #endif
-
  /* Define to 1 if you have the `strerror_r' function. */
  /* #undef HAVE_STRERROR_R */

--- 402,407 ----
diff --git a/src/include/port.h b/src/include/port.h
index d927561..bffc773 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern int    pg_printf(const char *fmt,...
*** 189,194 ****
--- 189,198 ----
  #endif
  #endif                            /* USE_REPL_SNPRINTF */

+ /* Replace strerror() with our own, somewhat more robust wrapper */
+ extern char *pg_strerror(int errnum);
+ #define strerror pg_strerror
+
  /* Portable prompt handling */
  extern void simple_prompt(const char *prompt, char *destination, size_t destlen,
                bool echo);
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index b398cd3..360dbdf 100644
*** a/src/include/port/win32_port.h
--- b/src/include/port/win32_port.h
*************** extern int    pgwin32_safestat(const char *
*** 322,329 ****
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants.  This allows elog.c to recognize them as being in the Winsock
!  * error code range and pass them off to pgwin32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
--- 322,329 ----
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants. This allows strerror.c to recognize them as being in the Winsock
!  * error code range and pass them off to win32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
*************** int            pgwin32_connect(SOCKET s, const st
*** 456,463 ****
  int            pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval
*timeout);
  int            pgwin32_recv(SOCKET s, char *buf, int len, int flags);
  int            pgwin32_send(SOCKET s, const void *buf, int len, int flags);
-
- const char *pgwin32_socket_strerror(int err);
  int            pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);

  extern int    pgwin32_noblock;
--- 456,461 ----
diff --git a/src/interfaces/ecpg/compatlib/.gitignore b/src/interfaces/ecpg/compatlib/.gitignore
index ad5ba13..8b9aa95 100644
*** a/src/interfaces/ecpg/compatlib/.gitignore
--- b/src/interfaces/ecpg/compatlib/.gitignore
***************
*** 2,5 ****
--- 2,6 ----
  /blibecpg_compatdll.def
  /exports.list
  /snprintf.c
+ /strerror.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index ebfd895..b7bd162 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,37 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

*************** submake-pgtypeslib:
*** 48,54 ****
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
--- 48,54 ----
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strerror.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
index 1619e97..545c106 100644
*** a/src/interfaces/ecpg/ecpglib/.gitignore
--- b/src/interfaces/ecpg/ecpglib/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /path.c
  /pgstrcasecmp.c
  /snprintf.c
+ /strerror.c
  /strlcpy.c
  /strnlen.c
  /thread.c
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index d25d248..005d25a 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,32 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

--- 26,32 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

*************** include $(top_srcdir)/src/Makefile.shlib
*** 57,63 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
--- 57,63 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % :
$(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
*************** uninstall: uninstall-lib
*** 74,79 ****

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
--- 74,79 ----

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/pgtypeslib/.gitignore b/src/interfaces/ecpg/pgtypeslib/.gitignore
index d5f0fae..b3fae08 100644
*** a/src/interfaces/ecpg/pgtypeslib/.gitignore
--- b/src/interfaces/ecpg/pgtypeslib/.gitignore
***************
*** 4,8 ****
--- 4,9 ----
  /pgstrcasecmp.c
  /rint.c
  /snprintf.c
+ /strerror.c
  /string.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 29264ce..18b2402 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,36 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
--- 30,36 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
*************** include $(top_srcdir)/src/Makefile.shlib
*** 45,51 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
--- 45,51 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index ec0122a..8324f4f 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strerror.o strlcpy.o
strnlen.owin32error.o win32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 5c2e6a8..6cc323a 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 27,33 ****
   */
  #undef _POSIX_C_SOURCE
  #undef _XOPEN_SOURCE
- #undef HAVE_STRERROR
  #undef HAVE_TZNAME

  /*
--- 27,32 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index d7467fb..b3a10ba 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,39 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/strerror.c b/src/port/strerror.c
index e92ebc9..e3393eb 100644
*** a/src/port/strerror.c
--- b/src/port/strerror.c
***************
*** 1,30 ****
! /* src/port/strerror.c */
!
! /*
!  * strerror - map error number to descriptive string
   *
!  * This version is obviously somewhat Unix-specific.
   *
!  * based on code by Henry Spencer
!  * modified for ANSI by D'Arcy J.M. Cain
   */
-
  #include "c.h"


! extern const char *const sys_errlist[];
! extern int    sys_nerr;

! const char *
! strerror(int errnum)
  {
!     static char buf[24];

!     if (errnum < 0 || errnum > sys_nerr)
      {
!         sprintf(buf, _("unrecognized error %d"), errnum);
!         return buf;
      }

!     return sys_errlist[errnum];
  }
--- 1,285 ----
! /*-------------------------------------------------------------------------
   *
!  * strerror.c
!  *      Replacement for standard strerror() function
   *
!  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
!  *
!  *
!  * IDENTIFICATION
!  *      src/port/strerror.c
!  *
!  *-------------------------------------------------------------------------
   */
  #include "c.h"

+ /*
+  * Within this file, "strerror" means the platform's function not pg_strerror
+  */
+ #undef strerror

! static char *get_errno_symbol(int errnum);
! #ifdef WIN32
! static char *win32_socket_strerror(int errnum);
! #endif

!
! /*
!  * A slightly cleaned-up version of strerror()
!  */
! char *
! pg_strerror(int errnum)
  {
!     /* this buffer is only used if strerror() and get_errno_symbol() fail */
!     static char errorstr_buf[48];
!     char       *str;

!     /* If it's a Windows Winsock error, that needs special handling */
! #ifdef WIN32
!     /* Winsock error code range, per WinError.h */
!     if (errnum >= 10000 && errnum <= 11999)
!         return win32_socket_strerror(errnum);
! #endif
!
!     /* Try the platform's strerror() */
!     str = strerror(errnum);
!
!     /*
!      * Some strerror()s return an empty string for out-of-range errno.  This
!      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
!      * back strings of question marks if libc cannot transcode the message to
!      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
!      * get_errno_symbol(), and if that fails, print the numeric errno.
!      */
!     if (str == NULL || *str == '\0' || *str == '?')
!         str = get_errno_symbol(errnum);
!
!     if (str == NULL)
      {
!         snprintf(errorstr_buf, sizeof(errorstr_buf),
!         /*------
!           translator: This string will be truncated at 47
!           characters expanded. */
!                  _("operating system error %d"), errnum);
!         str = errorstr_buf;
      }

!     return str;
  }
+
+ /*
+  * Returns a symbol (e.g. "ENOENT") for an errno code.
+  * Returns NULL if the code is unrecognized.
+  */
+ static char *
+ get_errno_symbol(int errnum)
+ {
+     switch (errnum)
+     {
+         case E2BIG:
+             return "E2BIG";
+         case EACCES:
+             return "EACCES";
+ #ifdef EADDRINUSE
+         case EADDRINUSE:
+             return "EADDRINUSE";
+ #endif
+ #ifdef EADDRNOTAVAIL
+         case EADDRNOTAVAIL:
+             return "EADDRNOTAVAIL";
+ #endif
+         case EAFNOSUPPORT:
+             return "EAFNOSUPPORT";
+ #ifdef EAGAIN
+         case EAGAIN:
+             return "EAGAIN";
+ #endif
+ #ifdef EALREADY
+         case EALREADY:
+             return "EALREADY";
+ #endif
+         case EBADF:
+             return "EBADF";
+ #ifdef EBADMSG
+         case EBADMSG:
+             return "EBADMSG";
+ #endif
+         case EBUSY:
+             return "EBUSY";
+         case ECHILD:
+             return "ECHILD";
+ #ifdef ECONNABORTED
+         case ECONNABORTED:
+             return "ECONNABORTED";
+ #endif
+         case ECONNREFUSED:
+             return "ECONNREFUSED";
+ #ifdef ECONNRESET
+         case ECONNRESET:
+             return "ECONNRESET";
+ #endif
+         case EDEADLK:
+             return "EDEADLK";
+         case EDOM:
+             return "EDOM";
+         case EEXIST:
+             return "EEXIST";
+         case EFAULT:
+             return "EFAULT";
+         case EFBIG:
+             return "EFBIG";
+ #ifdef EHOSTUNREACH
+         case EHOSTUNREACH:
+             return "EHOSTUNREACH";
+ #endif
+         case EIDRM:
+             return "EIDRM";
+         case EINPROGRESS:
+             return "EINPROGRESS";
+         case EINTR:
+             return "EINTR";
+         case EINVAL:
+             return "EINVAL";
+         case EIO:
+             return "EIO";
+ #ifdef EISCONN
+         case EISCONN:
+             return "EISCONN";
+ #endif
+         case EISDIR:
+             return "EISDIR";
+ #ifdef ELOOP
+         case ELOOP:
+             return "ELOOP";
+ #endif
+         case EMFILE:
+             return "EMFILE";
+         case EMLINK:
+             return "EMLINK";
+         case EMSGSIZE:
+             return "EMSGSIZE";
+         case ENAMETOOLONG:
+             return "ENAMETOOLONG";
+         case ENFILE:
+             return "ENFILE";
+         case ENOBUFS:
+             return "ENOBUFS";
+         case ENODEV:
+             return "ENODEV";
+         case ENOENT:
+             return "ENOENT";
+         case ENOEXEC:
+             return "ENOEXEC";
+         case ENOMEM:
+             return "ENOMEM";
+         case ENOSPC:
+             return "ENOSPC";
+         case ENOSYS:
+             return "ENOSYS";
+ #ifdef ENOTCONN
+         case ENOTCONN:
+             return "ENOTCONN";
+ #endif
+         case ENOTDIR:
+             return "ENOTDIR";
+ #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
+         case ENOTEMPTY:
+             return "ENOTEMPTY";
+ #endif
+ #ifdef ENOTSOCK
+         case ENOTSOCK:
+             return "ENOTSOCK";
+ #endif
+ #ifdef ENOTSUP
+         case ENOTSUP:
+             return "ENOTSUP";
+ #endif
+         case ENOTTY:
+             return "ENOTTY";
+         case ENXIO:
+             return "ENXIO";
+ #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
+         case EOPNOTSUPP:
+             return "EOPNOTSUPP";
+ #endif
+ #ifdef EOVERFLOW
+         case EOVERFLOW:
+             return "EOVERFLOW";
+ #endif
+         case EPERM:
+             return "EPERM";
+         case EPIPE:
+             return "EPIPE";
+         case EPROTONOSUPPORT:
+             return "EPROTONOSUPPORT";
+         case ERANGE:
+             return "ERANGE";
+ #ifdef EROFS
+         case EROFS:
+             return "EROFS";
+ #endif
+         case ESRCH:
+             return "ESRCH";
+ #ifdef ETIMEDOUT
+         case ETIMEDOUT:
+             return "ETIMEDOUT";
+ #endif
+ #ifdef ETXTBSY
+         case ETXTBSY:
+             return "ETXTBSY";
+ #endif
+ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+         case EWOULDBLOCK:
+             return "EWOULDBLOCK";
+ #endif
+         case EXDEV:
+             return "EXDEV";
+     }
+
+     return NULL;
+ }
+
+
+ #ifdef WIN32
+
+ /*
+  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
+  */
+ static char *
+ win32_socket_strerror(int errnum)
+ {
+     static char wserrbuf[256];
+     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
+
+     if (handleDLL == INVALID_HANDLE_VALUE)
+     {
+         handleDLL = LoadLibraryEx("netmsg.dll", NULL,
+                                   DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
+         if (handleDLL == NULL)
+         {
+             /* just treat this as an unrecognized error ... */
+             sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+             return wserrbuf;
+         }
+     }
+
+     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
+     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_FROM_HMODULE,
+                       handleDLL,
+                       errnum,
+                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+                       wserrbuf,
+                       sizeof(wserrbuf) - 1,
+                       NULL) == 0)
+     {
+         /* Failed to get id */
+         sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+     }
+
+     return wserrbuf;
+ }
+
+ #endif                            /* WIN32 */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 9ce03ce..2be72f8 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 96,104 ****
        chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
        srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c dlopen.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
--- 96,105 ----
        chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
        srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
+       dirent.c dlopen.c getopt.c getopt_long.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c strerror.c tar.c thread.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index eedaf12..fb58c94 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 20,34 ****
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", which matches the features glibc supports, notably
! # %m, 'z' and 'll' width modifiers ('ll' only matters if int64 requires it),
! # and argument order control if we're doing --enable-nls.  On platforms where
! # the native printf doesn't have 'z'/'ll' or arg control, we replace it with
! # src/port/snprintf.c which does, so that the only potential mismatch here is
! # whether or not %m is supported.  We need that for elog/ereport, so we live
! # with the fact that erroneous use of %m in plain printf calls won't be
! # detected.  (It appears that many versions of gcc/clang wouldn't report it
! # even if told to check according to plain printf archetype, anyway.)
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
--- 20,27 ----
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
diff --git a/config/c-library.m4 b/config/c-library.m4
index da7fa77..d371f1b 100644
*** a/config/c-library.m4
--- b/config/c-library.m4
*************** AC_DEFUN([PGAC_STRUCT_ADDRINFO],
*** 171,276 ****
  ])])# PGAC_STRUCT_ADDRINFO


- # PGAC_FUNC_SNPRINTF_ARG_CONTROL
- # ---------------------------------------
- # Determine if snprintf supports %1$ argument selection, e.g. %5$ selects
- # the fifth argument after the printf format string.
- # This is not in the C99 standard, but in the Single Unix Specification (SUS).
- # It is used in our language translation strings.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_ARG_CONTROL],
- [AC_MSG_CHECKING([whether snprintf supports argument control])
- AC_CACHE_VAL(pgac_cv_snprintf_arg_control,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_arg_control=yes],
- [pgac_cv_snprintf_arg_control=no],
- [pgac_cv_snprintf_arg_control=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_arg_control])
- ])# PGAC_FUNC_SNPRINTF_ARG_CONTROL
-
- # PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
- # ---------------------------------
- # Determine if snprintf supports the z length modifier for printing
- # size_t-sized variables. That's supported by C99 and POSIX but not
- # all platforms play ball, so we must test whether it's working.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT],
- [AC_MSG_CHECKING([whether snprintf supports the %z modifier])
- AC_CACHE_VAL(pgac_cv_snprintf_size_t_support,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_size_t_support=yes],
- [pgac_cv_snprintf_size_t_support=no],
- [pgac_cv_snprintf_size_t_support=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_size_t_support])
- ])# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-
- # PGAC_FUNC_SNPRINTF_C99_RESULT
- # -----------------------------
- # Determine whether snprintf returns the desired buffer length when
- # it overruns the actual buffer length.  That's required by C99 and POSIX
- # but ancient platforms don't behave that way, so we must test.
- # While we're at it, let's just verify that it doesn't physically overrun
- # the buffer.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_C99_RESULT],
- [AC_MSG_CHECKING([whether snprintf handles buffer overrun per C99])
- AC_CACHE_VAL(pgac_cv_snprintf_c99_result,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_c99_result=yes],
- [pgac_cv_snprintf_c99_result=no],
- [pgac_cv_snprintf_c99_result=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_c99_result])
- ])# PGAC_FUNC_SNPRINTF_C99_RESULT
-
-
  # PGAC_TYPE_LOCALE_T
  # ------------------
  # Check for the locale_t type and find the right header file.  macOS
--- 171,176 ----
diff --git a/configure b/configure
index 1aefc57..4eed2f4 100755
*** a/configure
--- b/configure
*************** $as_echo "#define HAVE_PS_STRINGS 1" >>c
*** 15325,15421 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   for ac_func in snprintf
- do :
-   ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
- if test "x$ac_cv_func_snprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_SNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
-   for ac_func in vsnprintf
- do :
-   ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
- if test "x$ac_cv_func_vsnprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_VSNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- ac_fn_c_check_decl "$LINENO" "snprintf" "ac_cv_have_decl_snprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_snprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_SNPRINTF $ac_have_decl
- _ACEOF
- ac_fn_c_check_decl "$LINENO" "vsnprintf" "ac_cv_have_decl_vsnprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_vsnprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_VSNPRINTF $ac_have_decl
- _ACEOF
-
-
-
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for isinf" >&5
  $as_echo_n "checking for isinf... " >&6; }
  if ${ac_cv_func_isinf+:} false; then :
--- 15325,15330 ----
*************** fi
*** 16133,16185 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports argument control" >&5
- $as_echo_n "checking whether snprintf supports argument control... " >&6; }
- if ${pgac_cv_snprintf_arg_control+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_arg_control=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_arg_control=yes
- else
-   pgac_cv_snprintf_arg_control=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_arg_control" >&5
- $as_echo "$pgac_cv_snprintf_arg_control" >&6; }
-
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-

  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long int is 64 bits" >&5
  $as_echo_n "checking whether long int is 64 bits... " >&6; }
--- 16042,16047 ----
*************** _ACEOF
*** 16359,16366 ****


  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 16221,16226 ----
*************** cat >>confdefs.h <<_ACEOF
*** 16373,16492 ****
  _ACEOF


- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports the %z modifier" >&5
- $as_echo_n "checking whether snprintf supports the %z modifier... " >&6; }
- if ${pgac_cv_snprintf_size_t_support+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_size_t_support=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_size_t_support=yes
- else
-   pgac_cv_snprintf_size_t_support=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_size_t_support" >&5
- $as_echo "$pgac_cv_snprintf_size_t_support" >&6; }
-
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf handles buffer overrun per C99" >&5
- $as_echo_n "checking whether snprintf handles buffer overrun per C99... " >&6; }
- if ${pgac_cv_snprintf_c99_result+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_c99_result=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_c99_result=yes
- else
-   pgac_cv_snprintf_c99_result=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_c99_result" >&5
- $as_echo "$pgac_cv_snprintf_c99_result" >&6; }
-
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-
- $as_echo "#define USE_REPL_SNPRINTF 1" >>confdefs.h
-
-   case " $LIBOBJS " in
-   *" snprintf.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
-  ;;
- esac
-
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
--- 16233,16238 ----
diff --git a/configure.in b/configure.in
index 3a23913..f4e0b07 100644
*** a/configure.in
--- b/configure.in
*************** if test "$pgac_cv_var_PS_STRINGS" = yes
*** 1595,1647 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   AC_CHECK_FUNCS(snprintf, [], pgac_need_repl_snprintf=yes)
-   AC_CHECK_FUNCS(vsnprintf, [], pgac_need_repl_snprintf=yes)
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- AC_CHECK_DECLS([snprintf, vsnprintf])
-
-
  dnl Cannot use AC_CHECK_FUNC because isinf may be a macro
  AC_CACHE_CHECK([for isinf], ac_cv_func_isinf,
  [AC_LINK_IFELSE([AC_LANG_PROGRAM([
--- 1595,1600 ----
*************** for the exact reason.]])],
*** 1811,1826 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_ARG_CONTROL
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-
  dnl Check to see if we have a working 64-bit integer type.
  dnl Since Postgres 8.4, we no longer support compilers without a working
  dnl 64-bit type; but we have to determine whether that type is called
--- 1764,1769 ----
*************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in
*** 1843,1850 ****
    [Define to the name of a signed 64-bit integer type.])

  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 1786,1791 ----
*************** fi
*** 1854,1883 ****
  AC_DEFINE_UNQUOTED(INT64_MODIFIER, $INT64_MODIFIER,
                     [Define to the appropriate printf length modifier for 64-bit ints.])

- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_C99_RESULT
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-   AC_DEFINE(USE_REPL_SNPRINTF, 1, [Use replacement snprintf() functions.])
-   AC_LIBOBJ(snprintf)
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  PGAC_C_BUILTIN_OP_OVERFLOW
--- 1795,1800 ----
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 798a823..df7e01f 100644
*** a/src/backend/lib/stringinfo.c
--- b/src/backend/lib/stringinfo.c
*************** resetStringInfo(StringInfo str)
*** 77,88 ****
--- 77,91 ----
  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);
*************** appendStringInfo(StringInfo str, const c
*** 105,110 ****
--- 108,116 ----
   * 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 22e5d87..b9c1130 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void write_csvlog(ErrorData *edat
*** 177,183 ****
  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);
--- 177,182 ----
*************** errcode_for_socket_access(void)
*** 705,717 ****
   */
  #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); \
--- 704,713 ----
*************** errcode_for_socket_access(void)
*** 722,736 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 718,731 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** errcode_for_socket_access(void)
*** 746,760 ****
  #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); \
--- 741,752 ----
*************** errcode_for_socket_access(void)
*** 765,779 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 757,770 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** send_message_to_frontend(ErrorData *edat
*** 3329,3387 ****


  /*
-  * 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
--- 3320,3325 ----
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 36e3383..267d86a 100644
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** archputs(const char *s, Archive *AH)
*** 1471,1476 ****
--- 1471,1477 ----
  int
  archprintf(Archive *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** archprintf(Archive *AH, const char *fmt,
*** 1483,1488 ****
--- 1484,1490 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
*************** RestoreOutput(ArchiveHandle *AH, OutputC
*** 1604,1609 ****
--- 1606,1612 ----
  int
  ahprintf(ArchiveHandle *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** ahprintf(ArchiveHandle *AH, const char *
*** 1616,1621 ****
--- 1619,1625 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 007be12..407a56d 100644
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
*************** _EndBlobs(ArchiveHandle *AH, TocEntry *t
*** 1026,1031 ****
--- 1026,1032 ----
  static int
  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** tarPrintf(ArchiveHandle *AH, TAR_MEMBER
*** 1038,1043 ****
--- 1039,1045 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 04465a1..2cf100f 100644
*** a/src/common/psprintf.c
--- b/src/common/psprintf.c
***************
*** 45,50 ****
--- 45,51 ----
  char *
  psprintf(const char *fmt,...)
  {
+     int            save_errno = errno;
      size_t        len = 128;        /* initial assumption about buffer size */

      for (;;)
*************** psprintf(const char *fmt,...)
*** 60,65 ****
--- 61,67 ----
          result = (char *) palloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          newlen = pvsnprintf(result, len, fmt, args);
          va_end(args);
*************** psprintf(const char *fmt,...)
*** 89,94 ****
--- 91,99 ----
   * Other error cases do not return, but exit via elog(ERROR) or exit().
   * Hence, this shouldn't be used inside libpq.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * 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.
diff --git a/src/include/c.h b/src/include/c.h
index 901d791..b255da6 100644
*** a/src/include/c.h
--- b/src/include/c.h
*************** typedef union PGAlignedXLogBlock
*** 1114,1127 ****
   * standard C library.
   */

- #if !HAVE_DECL_SNPRINTF
- extern int    snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
- #endif
-
- #if !HAVE_DECL_VSNPRINTF
- extern int    vsnprintf(char *str, size_t count, const char *fmt, va_list args);
- #endif
-
  #if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC
  extern int    fdatasync(int fildes);
  #endif
--- 1114,1119 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 705c52b..254fa80 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 166,175 ****
     don't. */
  #undef HAVE_DECL_RTLD_NOW

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_SNPRINTF
-
  /* Define to 1 if you have the declaration of `strlcat', and to 0 if you
     don't. */
  #undef HAVE_DECL_STRLCAT
--- 166,171 ----
***************
*** 194,203 ****
     don't. */
  #undef HAVE_DECL_SYS_SIGLIST

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_VSNPRINTF
-
  /* Define to 1 if you have the `dlopen' function. */
  #undef HAVE_DLOPEN

--- 190,195 ----
***************
*** 507,515 ****
  /* Define to 1 if you have the `shm_open' function. */
  #undef HAVE_SHM_OPEN

- /* Define to 1 if you have the `snprintf' function. */
- #undef HAVE_SNPRINTF
-
  /* Define to 1 if you have spinlocks. */
  #undef HAVE_SPINLOCKS

--- 499,504 ----
***************
*** 712,720 ****
  /* Define to 1 if you have the <uuid/uuid.h> header file. */
  #undef HAVE_UUID_UUID_H

- /* Define to 1 if you have the `vsnprintf' function. */
- #undef HAVE_VSNPRINTF
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #undef HAVE_WCHAR_H

--- 701,706 ----
***************
*** 923,931 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  #undef USE_PAM

- /* Use replacement snprintf() functions. */
- #undef USE_REPL_SNPRINTF
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #undef USE_SLICING_BY_8_CRC32C

--- 909,914 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index eafe377..92d648e 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 135,144 ****
     don't. */
  #define HAVE_DECL_RTLD_NOW 0

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_SNPRINTF 1
-
  /* Define to 1 if you have the declaration of `strnlen', and to 0 if you
     don't. */
  #define HAVE_DECL_STRNLEN 1
--- 135,140 ----
***************
*** 151,160 ****
     don't. */
  #define HAVE_DECL_STRTOULL 1

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_VSNPRINTF 1
-
  /* Define to 1 if you have the `dlopen' function. */
  /* #undef HAVE_DLOPEN */

--- 147,152 ----
***************
*** 373,381 ****
  /* Define to 1 if you have the `setsid' function. */
  /* #undef HAVE_SETSID */

- /* Define to 1 if you have the `snprintf' function. */
- /* #undef HAVE_SNPRINTF */
-
  /* Define to 1 if you have spinlocks. */
  #define HAVE_SPINLOCKS 1

--- 365,370 ----
***************
*** 553,561 ****
  /* Define to 1 if you have the <utime.h> header file. */
  #define HAVE_UTIME_H 1

- /* Define to 1 if you have the `vsnprintf' function. */
- #define HAVE_VSNPRINTF 1
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #define HAVE_WCHAR_H 1

--- 542,547 ----
***************
*** 712,720 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  /* #undef USE_PAM */

- /* Use replacement snprintf() functions. */
- #define USE_REPL_SNPRINTF 1
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #if (_MSC_VER < 1500)
  #define USE_SLICING_BY_8_CRC32C 1
--- 698,703 ----
diff --git a/src/include/port.h b/src/include/port.h
index bffc773..0f0cd28 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern unsigned char pg_tolower(unsigned
*** 134,140 ****
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! #ifdef USE_REPL_SNPRINTF

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
--- 134,145 ----
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! /*
!  * Beginning in v12, we always replace snprintf() and friends with our own
!  * implementation.  This symbol is no longer consulted by the core code,
!  * but keep it defined anyway in case any extensions are looking at it.
!  */
! #define USE_REPL_SNPRINTF 1

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
*************** extern int    pg_printf(const char *fmt,...
*** 187,193 ****
  #define fprintf            pg_fprintf
  #define printf            pg_printf
  #endif
- #endif                            /* USE_REPL_SNPRINTF */

  /* Replace strerror() with our own, somewhat more robust wrapper */
  extern char *pg_strerror(int errnum);
--- 192,197 ----
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index b7bd162..e07a7fa 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,38 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o snprintf.o strerror.o \
!     $(filter strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index 005d25a..b381623 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,33 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
!     $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
--- 26,33 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 18b2402..15d7f33 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,37 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
!     $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

--- 30,37 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter rint.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 8324f4f..a106088 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     snprintf.o strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o strlcpy.o strnlen.o win32error.o
win32setlocale.o,$(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 0814ec6..43c36c3 100644
*** a/src/interfaces/libpq/pqexpbuffer.c
--- b/src/interfaces/libpq/pqexpbuffer.c
*************** enlargePQExpBuffer(PQExpBuffer str, size
*** 233,238 ****
--- 233,239 ----
  void
  printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** printfPQExpBuffer(PQExpBuffer str, const
*** 244,249 ****
--- 245,251 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 261,266 ****
--- 263,269 ----
  void
  appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** appendPQExpBuffer(PQExpBuffer str, const
*** 270,275 ****
--- 273,279 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 281,286 ****
--- 285,293 ----
   * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
   * Attempt to format data and append it to str.  Returns true if done
   * (either successful or hard failure), false if need to retry.
+  *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
   */
  static bool
  appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index e6241f0..f8888a4 100644
*** a/src/pl/plperl/plperl.h
--- b/src/pl/plperl/plperl.h
***************
*** 29,39 ****
   * Sometimes perl carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  /*
   * ActivePerl 5.18 and later are MinGW-built, and their headers use GCC's
--- 29,36 ----
***************
*** 99,105 ****
  #endif

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 96,101 ----
***************
*** 113,119 ****
  #define vsnprintf        pg_vsnprintf
  #define snprintf        pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /* perl version and platform portability */
  #define NEED_eval_pv
--- 109,114 ----
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index e244104..3814a6c 100644
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** static bool set_string_attr(PyObject *ob
*** 46,51 ****
--- 46,52 ----
  void
  PLy_elog_impl(int elevel, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *xmsg;
      char       *tbmsg;
      int            tb_depth;
*************** PLy_elog_impl(int elevel, const char *fm
*** 96,101 ****
--- 97,103 ----
              va_list        ap;
              int            needed;

+             errno = save_errno;
              va_start(ap, fmt);
              needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
              va_end(ap);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 6cc323a..aefbfc2 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 33,43 ****
   * Sometimes python carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  #if defined(_MSC_VER) && defined(_DEBUG)
  /* Python uses #pragma to bring in a non-default libpython on VC++ if
--- 33,40 ----
*************** typedef int Py_ssize_t;
*** 124,130 ****
  #include <eval.h>

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 121,126 ----
*************** typedef int Py_ssize_t;
*** 138,144 ****
  #define vsnprintf                pg_vsnprintf
  #define snprintf                pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /*
   * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
--- 134,139 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index b3a10ba..a2ee8e2 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,40 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o snprintf.o sprompt.o strerror.o \
!     tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/README b/src/port/README
index 4ae96da..c446b46 100644
*** a/src/port/README
--- b/src/port/README
*************** and adding infrastructure to recompile t
*** 18,24 ****

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter snprintf.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
--- 18,24 ----

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter strlcat.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 851e2ae..63cec5d 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 64,69 ****
--- 64,77 ----
   *
   * 5. Space and '#' flags are not implemented.
   *
+  * In addition, we support some extensions over C99:
+  *
+  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
+  *
+  * 2. "%m" expands to the value of strerror(errno), where errno is the
+  * value that variable had at the start of the call.  This is a glibc
+  * extension, but a very useful one.
+  *
   *
   * Historically the result values of sprintf/snprintf varied across platforms.
   * This implementation now follows the C99 standard:
*************** static void flushbuffer(PrintfTarget *ta
*** 155,160 ****
--- 163,175 ----
  static void dopr(PrintfTarget *target, const char *format, va_list args);


+ /*
+  * Externally visible entry points.
+  *
+  * All of these are just wrappers around dopr().  Note it's essential that
+  * they not change the value of "errno" before reaching dopr().
+  */
+
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** static void trailing_pad(int *padlen, Pr
*** 315,325 ****


  /*
!  * dopr(): poor man's version of doprintf
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
--- 330,341 ----


  /*
!  * dopr(): the guts of *printf for all cases.
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
+     int            save_errno = errno;
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
*************** nextch1:
*** 497,502 ****
--- 513,519 ----
                  else
                      have_non_dollar = true;
                  break;
+             case 'm':
              case '%':
                  break;
          }
*************** nextch2:
*** 802,807 ****
--- 819,831 ----
                           precision, pointflag,
                           target);
                  break;
+             case 'm':
+                 {
+                     const char *errm = strerror(save_errno);
+
+                     dostr(errm, strlen(errm), target);
+                 }
+                 break;
              case '%':
                  dopr_outch('%', target);
                  break;

Re: Allowing printf("%m") only where it actually works

От
Michael Paquier
Дата:
On Wed, Sep 12, 2018 at 01:40:01PM -0400, Tom Lane wrote:
> Michael Paquier <michael@paquier.xyz> writes:
> > I would have liked to look at this patch in details, but it failed to
> > apply.  Could you rebase?
>
> Ah, yeah, the dlopen patch touched a couple of the same places.
> Rebase attached --- no substantive changes.

-       if (handleDLL == NULL)
-           ereport(FATAL,
-                   (errmsg_internal("could not load netmsg.dll: error
-            code %lu", GetLastError())));

In 0001, this is replaced by a non-FATAL error for the backend, which
does not seem like a good idea to me because the user loses visibility
with this DDL which canot be loaded.  I still have to see this error...

And about 0002.  I am surprised by the amount of cleanup that the
removal of all the special wrappers for %m gives, especially
expand_fmt_string.  So +1 for the concept.  I have been testing both
patches individually on Windows and compilation, as well as tests, do
not show any issues.  The tests have been done only with MSVC.

Could you drop the configure checks for snprintf and vsnprintf in a
separate patch?  The cleanup of %m and the removal of those switches
should be treated separatly in my opinion.
--
Michael

Вложения

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Michael Paquier <michael@paquier.xyz> writes:
> On Wed, Sep 12, 2018 at 01:40:01PM -0400, Tom Lane wrote:
>> Rebase attached --- no substantive changes.

> -       if (handleDLL == NULL)
> -           ereport(FATAL,
> -                   (errmsg_internal("could not load netmsg.dll: error
> -            code %lu", GetLastError())));

> In 0001, this is replaced by a non-FATAL error for the backend, which
> does not seem like a good idea to me because the user loses visibility
> with this DDL which canot be loaded.  I still have to see this error...

Well, we have to change the code somehow to make it usable in frontend
as well as backend.  And we can *not* have it do exit(1) in libpq.
So the solution I chose was to make it act the same as if FormatMessage
were to fail.  I don't find this behavior unreasonable: what is really
important is the original error code, not whether we were able to
pretty-print it.  I think the ereport(FATAL) coding is a pretty darn
bad idea even in the backend.

> Could you drop the configure checks for snprintf and vsnprintf in a
> separate patch?  The cleanup of %m and the removal of those switches
> should be treated separatly in my opinion.

Seems a bit make-worky, but here you go.  0001 is the same as before
(but rebased up to today, so some line numbers change).  0002
changes things so that we always use our snprintf, removing all the
configure logic associated with that.  0003 implements %m in snprintf.c
and adjusts our various printf-wrapper functions to ensure that they
pass errno through reliably.  0004 changes elog.c to rely on %m being
implemented below it.

            regards, tom lane

diff --git a/configure b/configure
index 9b30402..afbc142 100755
*** a/configure
--- b/configure
*************** esac
*** 15635,15653 ****

  fi

- ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
- if test "x$ac_cv_func_strerror" = xyes; then :
-   $as_echo "#define HAVE_STRERROR 1" >>confdefs.h
-
- else
-   case " $LIBOBJS " in
-   *" strerror.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS strerror.$ac_objext"
-  ;;
- esac
-
- fi
-
  ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
  if test "x$ac_cv_func_strlcat" = xyes; then :
    $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
--- 15635,15640 ----
diff --git a/configure.in b/configure.in
index 2e60a89..6973b77 100644
*** a/configure.in
--- b/configure.in
*************** else
*** 1678,1684 ****
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strerror strlcat strlcpy
strnlen])

  case $host_os in

--- 1678,1684 ----
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi

! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strlcat strlcpy strnlen])

  case $host_os in

diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index f4356fe..af35cfb 100644
*** a/src/backend/port/win32/socket.c
--- b/src/backend/port/win32/socket.c
*************** pgwin32_select(int nfds, fd_set *readfds
*** 690,728 ****
          memcpy(writefds, &outwritefds, sizeof(fd_set));
      return nummatches;
  }
-
-
- /*
-  * Return win32 error string, since strerror can't
-  * handle winsock codes
-  */
- static char wserrbuf[256];
- const char *
- pgwin32_socket_strerror(int err)
- {
-     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
-
-     if (handleDLL == INVALID_HANDLE_VALUE)
-     {
-         handleDLL = LoadLibraryEx("netmsg.dll", NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
-         if (handleDLL == NULL)
-             ereport(FATAL,
-                     (errmsg_internal("could not load netmsg.dll: error code %lu", GetLastError())));
-     }
-
-     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
-     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
-                       FORMAT_MESSAGE_FROM_SYSTEM |
-                       FORMAT_MESSAGE_FROM_HMODULE,
-                       handleDLL,
-                       err,
-                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
-                       wserrbuf,
-                       sizeof(wserrbuf) - 1,
-                       NULL) == 0)
-     {
-         /* Failed to get id */
-         sprintf(wserrbuf, "unrecognized winsock error %d", err);
-     }
-     return wserrbuf;
- }
--- 690,692 ----
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 16531f7..22e5d87 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void send_message_to_server_log(E
*** 178,185 ****
  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 *useful_strerror(int errnum);
- static const char *get_errno_symbol(int errnum);
  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);
--- 178,183 ----
*************** expand_fmt_string(const char *fmt, Error
*** 3360,3366 ****
                   */
                  const char *cp2;

!                 cp2 = useful_strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
--- 3358,3364 ----
                   */
                  const char *cp2;

!                 cp2 = strerror(edata->saved_errno);
                  for (; *cp2; cp2++)
                  {
                      if (*cp2 == '%')
*************** expand_fmt_string(const char *fmt, Error
*** 3384,3602 ****


  /*
-  * A slightly cleaned-up version of strerror()
-  */
- static const char *
- useful_strerror(int errnum)
- {
-     /* this buffer is only used if strerror() and get_errno_symbol() fail */
-     static char errorstr_buf[48];
-     const char *str;
-
- #ifdef WIN32
-     /* Winsock error code range, per WinError.h */
-     if (errnum >= 10000 && errnum <= 11999)
-         return pgwin32_socket_strerror(errnum);
- #endif
-     str = strerror(errnum);
-
-     /*
-      * Some strerror()s return an empty string for out-of-range errno.  This
-      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
-      * back strings of question marks if libc cannot transcode the message to
-      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
-      * get_errno_symbol(), and if that fails, print the numeric errno.
-      */
-     if (str == NULL || *str == '\0' || *str == '?')
-         str = get_errno_symbol(errnum);
-
-     if (str == NULL)
-     {
-         snprintf(errorstr_buf, sizeof(errorstr_buf),
-         /*------
-           translator: This string will be truncated at 47
-           characters expanded. */
-                  _("operating system error %d"), errnum);
-         str = errorstr_buf;
-     }
-
-     return str;
- }
-
- /*
-  * Returns a symbol (e.g. "ENOENT") for an errno code.
-  * Returns NULL if the code is unrecognized.
-  */
- static const char *
- get_errno_symbol(int errnum)
- {
-     switch (errnum)
-     {
-         case E2BIG:
-             return "E2BIG";
-         case EACCES:
-             return "EACCES";
- #ifdef EADDRINUSE
-         case EADDRINUSE:
-             return "EADDRINUSE";
- #endif
- #ifdef EADDRNOTAVAIL
-         case EADDRNOTAVAIL:
-             return "EADDRNOTAVAIL";
- #endif
-         case EAFNOSUPPORT:
-             return "EAFNOSUPPORT";
- #ifdef EAGAIN
-         case EAGAIN:
-             return "EAGAIN";
- #endif
- #ifdef EALREADY
-         case EALREADY:
-             return "EALREADY";
- #endif
-         case EBADF:
-             return "EBADF";
- #ifdef EBADMSG
-         case EBADMSG:
-             return "EBADMSG";
- #endif
-         case EBUSY:
-             return "EBUSY";
-         case ECHILD:
-             return "ECHILD";
- #ifdef ECONNABORTED
-         case ECONNABORTED:
-             return "ECONNABORTED";
- #endif
-         case ECONNREFUSED:
-             return "ECONNREFUSED";
- #ifdef ECONNRESET
-         case ECONNRESET:
-             return "ECONNRESET";
- #endif
-         case EDEADLK:
-             return "EDEADLK";
-         case EDOM:
-             return "EDOM";
-         case EEXIST:
-             return "EEXIST";
-         case EFAULT:
-             return "EFAULT";
-         case EFBIG:
-             return "EFBIG";
- #ifdef EHOSTUNREACH
-         case EHOSTUNREACH:
-             return "EHOSTUNREACH";
- #endif
-         case EIDRM:
-             return "EIDRM";
-         case EINPROGRESS:
-             return "EINPROGRESS";
-         case EINTR:
-             return "EINTR";
-         case EINVAL:
-             return "EINVAL";
-         case EIO:
-             return "EIO";
- #ifdef EISCONN
-         case EISCONN:
-             return "EISCONN";
- #endif
-         case EISDIR:
-             return "EISDIR";
- #ifdef ELOOP
-         case ELOOP:
-             return "ELOOP";
- #endif
-         case EMFILE:
-             return "EMFILE";
-         case EMLINK:
-             return "EMLINK";
-         case EMSGSIZE:
-             return "EMSGSIZE";
-         case ENAMETOOLONG:
-             return "ENAMETOOLONG";
-         case ENFILE:
-             return "ENFILE";
-         case ENOBUFS:
-             return "ENOBUFS";
-         case ENODEV:
-             return "ENODEV";
-         case ENOENT:
-             return "ENOENT";
-         case ENOEXEC:
-             return "ENOEXEC";
-         case ENOMEM:
-             return "ENOMEM";
-         case ENOSPC:
-             return "ENOSPC";
-         case ENOSYS:
-             return "ENOSYS";
- #ifdef ENOTCONN
-         case ENOTCONN:
-             return "ENOTCONN";
- #endif
-         case ENOTDIR:
-             return "ENOTDIR";
- #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
-         case ENOTEMPTY:
-             return "ENOTEMPTY";
- #endif
- #ifdef ENOTSOCK
-         case ENOTSOCK:
-             return "ENOTSOCK";
- #endif
- #ifdef ENOTSUP
-         case ENOTSUP:
-             return "ENOTSUP";
- #endif
-         case ENOTTY:
-             return "ENOTTY";
-         case ENXIO:
-             return "ENXIO";
- #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
-         case EOPNOTSUPP:
-             return "EOPNOTSUPP";
- #endif
- #ifdef EOVERFLOW
-         case EOVERFLOW:
-             return "EOVERFLOW";
- #endif
-         case EPERM:
-             return "EPERM";
-         case EPIPE:
-             return "EPIPE";
-         case EPROTONOSUPPORT:
-             return "EPROTONOSUPPORT";
-         case ERANGE:
-             return "ERANGE";
- #ifdef EROFS
-         case EROFS:
-             return "EROFS";
- #endif
-         case ESRCH:
-             return "ESRCH";
- #ifdef ETIMEDOUT
-         case ETIMEDOUT:
-             return "ETIMEDOUT";
- #endif
- #ifdef ETXTBSY
-         case ETXTBSY:
-             return "ETXTBSY";
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-         case EWOULDBLOCK:
-             return "EWOULDBLOCK";
- #endif
-         case EXDEV:
-             return "EXDEV";
-     }
-
-     return NULL;
- }
-
-
- /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
--- 3382,3387 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4094e22..705c52b 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 531,539 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #undef HAVE_STDLIB_H

- /* Define to 1 if you have the `strerror' function. */
- #undef HAVE_STRERROR
-
  /* Define to 1 if you have the `strerror_r' function. */
  #undef HAVE_STRERROR_R

--- 531,536 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 6618b43..eafe377 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 402,412 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #define HAVE_STDLIB_H 1

- /* Define to 1 if you have the `strerror' function. */
- #ifndef HAVE_STRERROR
- #define HAVE_STRERROR 1
- #endif
-
  /* Define to 1 if you have the `strerror_r' function. */
  /* #undef HAVE_STRERROR_R */

--- 402,407 ----
diff --git a/src/include/port.h b/src/include/port.h
index 2522ebc..cc2648c 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern int    pg_printf(const char *fmt,...
*** 189,194 ****
--- 189,198 ----
  #endif
  #endif                            /* USE_REPL_SNPRINTF */

+ /* Replace strerror() with our own, somewhat more robust wrapper */
+ extern char *pg_strerror(int errnum);
+ #define strerror pg_strerror
+
  /* Portable prompt handling */
  extern void simple_prompt(const char *prompt, char *destination, size_t destlen,
                bool echo);
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index b398cd3..360dbdf 100644
*** a/src/include/port/win32_port.h
--- b/src/include/port/win32_port.h
*************** extern int    pgwin32_safestat(const char *
*** 322,329 ****
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants.  This allows elog.c to recognize them as being in the Winsock
!  * error code range and pass them off to pgwin32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
--- 322,329 ----
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants. This allows strerror.c to recognize them as being in the Winsock
!  * error code range and pass them off to win32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
*************** int            pgwin32_connect(SOCKET s, const st
*** 456,463 ****
  int            pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval
*timeout);
  int            pgwin32_recv(SOCKET s, char *buf, int len, int flags);
  int            pgwin32_send(SOCKET s, const void *buf, int len, int flags);
-
- const char *pgwin32_socket_strerror(int err);
  int            pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);

  extern int    pgwin32_noblock;
--- 456,461 ----
diff --git a/src/interfaces/ecpg/compatlib/.gitignore b/src/interfaces/ecpg/compatlib/.gitignore
index ad5ba13..8b9aa95 100644
*** a/src/interfaces/ecpg/compatlib/.gitignore
--- b/src/interfaces/ecpg/compatlib/.gitignore
***************
*** 2,5 ****
--- 2,6 ----
  /blibecpg_compatdll.def
  /exports.list
  /snprintf.c
+ /strerror.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index ebfd895..b7bd162 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,37 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

*************** submake-pgtypeslib:
*** 48,54 ****
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
--- 48,54 ----
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib

! snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  install: all installdirs install-lib
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) snprintf.c strerror.c strnlen.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
index 1619e97..545c106 100644
*** a/src/interfaces/ecpg/ecpglib/.gitignore
--- b/src/interfaces/ecpg/ecpglib/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /path.c
  /pgstrcasecmp.c
  /snprintf.c
+ /strerror.c
  /strlcpy.c
  /strnlen.c
  /thread.c
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index d25d248..005d25a 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,32 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

--- 26,32 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
      $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

*************** include $(top_srcdir)/src/Makefile.shlib
*** 57,63 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
--- 57,63 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c: % :
$(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
*************** uninstall: uninstall-lib
*** 74,79 ****

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
--- 74,79 ----

  clean distclean: clean-lib
      rm -f $(OBJS)
!     rm -f path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/pgtypeslib/.gitignore b/src/interfaces/ecpg/pgtypeslib/.gitignore
index d5f0fae..b3fae08 100644
*** a/src/interfaces/ecpg/pgtypeslib/.gitignore
--- b/src/interfaces/ecpg/pgtypeslib/.gitignore
***************
*** 4,8 ****
--- 4,9 ----
  /pgstrcasecmp.c
  /rint.c
  /snprintf.c
+ /strerror.c
  /string.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 29264ce..18b2402 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,36 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
--- 30,36 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
      $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)
*************** include $(top_srcdir)/src/Makefile.shlib
*** 45,51 ****
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
--- 45,51 ----
  # necessarily use the same object files as the backend uses. Instead,
  # symlink the source files in here and build our own object file.

! pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
      rm -f $@ && $(LN_S) $< .

  string.c: % : $(top_srcdir)/src/common/%
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib

  clean distclean: clean-lib
!     rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c string.c

  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index ec0122a..8324f4f 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strerror.o strlcpy.o
strnlen.owin32error.o win32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 5c2e6a8..6cc323a 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 27,33 ****
   */
  #undef _POSIX_C_SOURCE
  #undef _XOPEN_SOURCE
- #undef HAVE_STRERROR
  #undef HAVE_TZNAME

  /*
--- 27,32 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index d7467fb..b3a10ba 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,39 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/strerror.c b/src/port/strerror.c
index e92ebc9..e3393eb 100644
*** a/src/port/strerror.c
--- b/src/port/strerror.c
***************
*** 1,30 ****
! /* src/port/strerror.c */
!
! /*
!  * strerror - map error number to descriptive string
   *
!  * This version is obviously somewhat Unix-specific.
   *
!  * based on code by Henry Spencer
!  * modified for ANSI by D'Arcy J.M. Cain
   */
-
  #include "c.h"


! extern const char *const sys_errlist[];
! extern int    sys_nerr;

! const char *
! strerror(int errnum)
  {
!     static char buf[24];

!     if (errnum < 0 || errnum > sys_nerr)
      {
!         sprintf(buf, _("unrecognized error %d"), errnum);
!         return buf;
      }

!     return sys_errlist[errnum];
  }
--- 1,285 ----
! /*-------------------------------------------------------------------------
   *
!  * strerror.c
!  *      Replacement for standard strerror() function
   *
!  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
!  *
!  *
!  * IDENTIFICATION
!  *      src/port/strerror.c
!  *
!  *-------------------------------------------------------------------------
   */
  #include "c.h"

+ /*
+  * Within this file, "strerror" means the platform's function not pg_strerror
+  */
+ #undef strerror

! static char *get_errno_symbol(int errnum);
! #ifdef WIN32
! static char *win32_socket_strerror(int errnum);
! #endif

!
! /*
!  * A slightly cleaned-up version of strerror()
!  */
! char *
! pg_strerror(int errnum)
  {
!     /* this buffer is only used if strerror() and get_errno_symbol() fail */
!     static char errorstr_buf[48];
!     char       *str;

!     /* If it's a Windows Winsock error, that needs special handling */
! #ifdef WIN32
!     /* Winsock error code range, per WinError.h */
!     if (errnum >= 10000 && errnum <= 11999)
!         return win32_socket_strerror(errnum);
! #endif
!
!     /* Try the platform's strerror() */
!     str = strerror(errnum);
!
!     /*
!      * Some strerror()s return an empty string for out-of-range errno.  This
!      * is ANSI C spec compliant, but not exactly useful.  Also, we may get
!      * back strings of question marks if libc cannot transcode the message to
!      * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
!      * get_errno_symbol(), and if that fails, print the numeric errno.
!      */
!     if (str == NULL || *str == '\0' || *str == '?')
!         str = get_errno_symbol(errnum);
!
!     if (str == NULL)
      {
!         snprintf(errorstr_buf, sizeof(errorstr_buf),
!         /*------
!           translator: This string will be truncated at 47
!           characters expanded. */
!                  _("operating system error %d"), errnum);
!         str = errorstr_buf;
      }

!     return str;
  }
+
+ /*
+  * Returns a symbol (e.g. "ENOENT") for an errno code.
+  * Returns NULL if the code is unrecognized.
+  */
+ static char *
+ get_errno_symbol(int errnum)
+ {
+     switch (errnum)
+     {
+         case E2BIG:
+             return "E2BIG";
+         case EACCES:
+             return "EACCES";
+ #ifdef EADDRINUSE
+         case EADDRINUSE:
+             return "EADDRINUSE";
+ #endif
+ #ifdef EADDRNOTAVAIL
+         case EADDRNOTAVAIL:
+             return "EADDRNOTAVAIL";
+ #endif
+         case EAFNOSUPPORT:
+             return "EAFNOSUPPORT";
+ #ifdef EAGAIN
+         case EAGAIN:
+             return "EAGAIN";
+ #endif
+ #ifdef EALREADY
+         case EALREADY:
+             return "EALREADY";
+ #endif
+         case EBADF:
+             return "EBADF";
+ #ifdef EBADMSG
+         case EBADMSG:
+             return "EBADMSG";
+ #endif
+         case EBUSY:
+             return "EBUSY";
+         case ECHILD:
+             return "ECHILD";
+ #ifdef ECONNABORTED
+         case ECONNABORTED:
+             return "ECONNABORTED";
+ #endif
+         case ECONNREFUSED:
+             return "ECONNREFUSED";
+ #ifdef ECONNRESET
+         case ECONNRESET:
+             return "ECONNRESET";
+ #endif
+         case EDEADLK:
+             return "EDEADLK";
+         case EDOM:
+             return "EDOM";
+         case EEXIST:
+             return "EEXIST";
+         case EFAULT:
+             return "EFAULT";
+         case EFBIG:
+             return "EFBIG";
+ #ifdef EHOSTUNREACH
+         case EHOSTUNREACH:
+             return "EHOSTUNREACH";
+ #endif
+         case EIDRM:
+             return "EIDRM";
+         case EINPROGRESS:
+             return "EINPROGRESS";
+         case EINTR:
+             return "EINTR";
+         case EINVAL:
+             return "EINVAL";
+         case EIO:
+             return "EIO";
+ #ifdef EISCONN
+         case EISCONN:
+             return "EISCONN";
+ #endif
+         case EISDIR:
+             return "EISDIR";
+ #ifdef ELOOP
+         case ELOOP:
+             return "ELOOP";
+ #endif
+         case EMFILE:
+             return "EMFILE";
+         case EMLINK:
+             return "EMLINK";
+         case EMSGSIZE:
+             return "EMSGSIZE";
+         case ENAMETOOLONG:
+             return "ENAMETOOLONG";
+         case ENFILE:
+             return "ENFILE";
+         case ENOBUFS:
+             return "ENOBUFS";
+         case ENODEV:
+             return "ENODEV";
+         case ENOENT:
+             return "ENOENT";
+         case ENOEXEC:
+             return "ENOEXEC";
+         case ENOMEM:
+             return "ENOMEM";
+         case ENOSPC:
+             return "ENOSPC";
+         case ENOSYS:
+             return "ENOSYS";
+ #ifdef ENOTCONN
+         case ENOTCONN:
+             return "ENOTCONN";
+ #endif
+         case ENOTDIR:
+             return "ENOTDIR";
+ #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
+         case ENOTEMPTY:
+             return "ENOTEMPTY";
+ #endif
+ #ifdef ENOTSOCK
+         case ENOTSOCK:
+             return "ENOTSOCK";
+ #endif
+ #ifdef ENOTSUP
+         case ENOTSUP:
+             return "ENOTSUP";
+ #endif
+         case ENOTTY:
+             return "ENOTTY";
+         case ENXIO:
+             return "ENXIO";
+ #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
+         case EOPNOTSUPP:
+             return "EOPNOTSUPP";
+ #endif
+ #ifdef EOVERFLOW
+         case EOVERFLOW:
+             return "EOVERFLOW";
+ #endif
+         case EPERM:
+             return "EPERM";
+         case EPIPE:
+             return "EPIPE";
+         case EPROTONOSUPPORT:
+             return "EPROTONOSUPPORT";
+         case ERANGE:
+             return "ERANGE";
+ #ifdef EROFS
+         case EROFS:
+             return "EROFS";
+ #endif
+         case ESRCH:
+             return "ESRCH";
+ #ifdef ETIMEDOUT
+         case ETIMEDOUT:
+             return "ETIMEDOUT";
+ #endif
+ #ifdef ETXTBSY
+         case ETXTBSY:
+             return "ETXTBSY";
+ #endif
+ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+         case EWOULDBLOCK:
+             return "EWOULDBLOCK";
+ #endif
+         case EXDEV:
+             return "EXDEV";
+     }
+
+     return NULL;
+ }
+
+
+ #ifdef WIN32
+
+ /*
+  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
+  */
+ static char *
+ win32_socket_strerror(int errnum)
+ {
+     static char wserrbuf[256];
+     static HANDLE handleDLL = INVALID_HANDLE_VALUE;
+
+     if (handleDLL == INVALID_HANDLE_VALUE)
+     {
+         handleDLL = LoadLibraryEx("netmsg.dll", NULL,
+                                   DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
+         if (handleDLL == NULL)
+         {
+             /* just treat this as an unrecognized error ... */
+             sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+             return wserrbuf;
+         }
+     }
+
+     ZeroMemory(&wserrbuf, sizeof(wserrbuf));
+     if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_FROM_HMODULE,
+                       handleDLL,
+                       errnum,
+                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+                       wserrbuf,
+                       sizeof(wserrbuf) - 1,
+                       NULL) == 0)
+     {
+         /* Failed to get id */
+         sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+     }
+
+     return wserrbuf;
+ }
+
+ #endif                            /* WIN32 */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 6484a67..640a4b7 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 96,104 ****
        chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
        srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c dlopen.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
--- 96,105 ----
        chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
        srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
        erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
+       dirent.c dlopen.c getopt.c getopt_long.c
        pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
        pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
!       sprompt.c strerror.c tar.c thread.c
        win32env.c win32error.c win32security.c win32setlocale.c);

      push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index eedaf12..fb58c94 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 20,34 ****
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", which matches the features glibc supports, notably
! # %m, 'z' and 'll' width modifiers ('ll' only matters if int64 requires it),
! # and argument order control if we're doing --enable-nls.  On platforms where
! # the native printf doesn't have 'z'/'ll' or arg control, we replace it with
! # src/port/snprintf.c which does, so that the only potential mismatch here is
! # whether or not %m is supported.  We need that for elog/ereport, so we live
! # with the fact that erroneous use of %m in plain printf calls won't be
! # detected.  (It appears that many versions of gcc/clang wouldn't report it
! # even if told to check according to plain printf archetype, anyway.)
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
--- 20,27 ----
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
diff --git a/config/c-library.m4 b/config/c-library.m4
index da7fa77..d371f1b 100644
*** a/config/c-library.m4
--- b/config/c-library.m4
*************** AC_DEFUN([PGAC_STRUCT_ADDRINFO],
*** 171,276 ****
  ])])# PGAC_STRUCT_ADDRINFO


- # PGAC_FUNC_SNPRINTF_ARG_CONTROL
- # ---------------------------------------
- # Determine if snprintf supports %1$ argument selection, e.g. %5$ selects
- # the fifth argument after the printf format string.
- # This is not in the C99 standard, but in the Single Unix Specification (SUS).
- # It is used in our language translation strings.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_ARG_CONTROL],
- [AC_MSG_CHECKING([whether snprintf supports argument control])
- AC_CACHE_VAL(pgac_cv_snprintf_arg_control,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_arg_control=yes],
- [pgac_cv_snprintf_arg_control=no],
- [pgac_cv_snprintf_arg_control=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_arg_control])
- ])# PGAC_FUNC_SNPRINTF_ARG_CONTROL
-
- # PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
- # ---------------------------------
- # Determine if snprintf supports the z length modifier for printing
- # size_t-sized variables. That's supported by C99 and POSIX but not
- # all platforms play ball, so we must test whether it's working.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT],
- [AC_MSG_CHECKING([whether snprintf supports the %z modifier])
- AC_CACHE_VAL(pgac_cv_snprintf_size_t_support,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_size_t_support=yes],
- [pgac_cv_snprintf_size_t_support=no],
- [pgac_cv_snprintf_size_t_support=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_size_t_support])
- ])# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-
- # PGAC_FUNC_SNPRINTF_C99_RESULT
- # -----------------------------
- # Determine whether snprintf returns the desired buffer length when
- # it overruns the actual buffer length.  That's required by C99 and POSIX
- # but ancient platforms don't behave that way, so we must test.
- # While we're at it, let's just verify that it doesn't physically overrun
- # the buffer.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_C99_RESULT],
- [AC_MSG_CHECKING([whether snprintf handles buffer overrun per C99])
- AC_CACHE_VAL(pgac_cv_snprintf_c99_result,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_c99_result=yes],
- [pgac_cv_snprintf_c99_result=no],
- [pgac_cv_snprintf_c99_result=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_c99_result])
- ])# PGAC_FUNC_SNPRINTF_C99_RESULT
-
-
  # PGAC_TYPE_LOCALE_T
  # ------------------
  # Check for the locale_t type and find the right header file.  macOS
--- 171,176 ----
diff --git a/configure b/configure
index afbc142..31082da 100755
*** a/configure
--- b/configure
*************** $as_echo "#define HAVE_PS_STRINGS 1" >>c
*** 15358,15454 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   for ac_func in snprintf
- do :
-   ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
- if test "x$ac_cv_func_snprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_SNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
-   for ac_func in vsnprintf
- do :
-   ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
- if test "x$ac_cv_func_vsnprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_VSNPRINTF 1
- _ACEOF
-
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
-
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- ac_fn_c_check_decl "$LINENO" "snprintf" "ac_cv_have_decl_snprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_snprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_SNPRINTF $ac_have_decl
- _ACEOF
- ac_fn_c_check_decl "$LINENO" "vsnprintf" "ac_cv_have_decl_vsnprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_vsnprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
-
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_VSNPRINTF $ac_have_decl
- _ACEOF
-
-
-
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for isinf" >&5
  $as_echo_n "checking for isinf... " >&6; }
  if ${ac_cv_func_isinf+:} false; then :
--- 15358,15363 ----
*************** fi
*** 16166,16218 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports argument control" >&5
- $as_echo_n "checking whether snprintf supports argument control... " >&6; }
- if ${pgac_cv_snprintf_arg_control+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_arg_control=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[100];
-
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_arg_control=yes
- else
-   pgac_cv_snprintf_arg_control=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_arg_control" >&5
- $as_echo "$pgac_cv_snprintf_arg_control" >&6; }
-
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-

  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long int is 64 bits" >&5
  $as_echo_n "checking whether long int is 64 bits... " >&6; }
--- 16075,16080 ----
*************** _ACEOF
*** 16392,16399 ****


  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 16254,16259 ----
*************** cat >>confdefs.h <<_ACEOF
*** 16406,16525 ****
  _ACEOF


- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports the %z modifier" >&5
- $as_echo_n "checking whether snprintf supports the %z modifier... " >&6; }
- if ${pgac_cv_snprintf_size_t_support+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_size_t_support=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char bufz[100];
-   char buf64[100];
-
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_size_t_support=yes
- else
-   pgac_cv_snprintf_size_t_support=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_size_t_support" >&5
- $as_echo "$pgac_cv_snprintf_size_t_support" >&6; }
-
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf handles buffer overrun per C99" >&5
- $as_echo_n "checking whether snprintf handles buffer overrun per C99... " >&6; }
- if ${pgac_cv_snprintf_c99_result+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_c99_result=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
-
- int main()
- {
-   char buf[10];
-
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_c99_result=yes
- else
-   pgac_cv_snprintf_c99_result=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
-
-
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_c99_result" >&5
- $as_echo "$pgac_cv_snprintf_c99_result" >&6; }
-
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-
- $as_echo "#define USE_REPL_SNPRINTF 1" >>confdefs.h
-
-   case " $LIBOBJS " in
-   *" snprintf.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
-  ;;
- esac
-
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
--- 16266,16271 ----
diff --git a/configure.in b/configure.in
index 6973b77..251759e 100644
*** a/configure.in
--- b/configure.in
*************** if test "$pgac_cv_var_PS_STRINGS" = yes
*** 1613,1665 ****
  fi


- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
-
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   AC_CHECK_FUNCS(snprintf, [], pgac_need_repl_snprintf=yes)
-   AC_CHECK_FUNCS(vsnprintf, [], pgac_need_repl_snprintf=yes)
- fi
-
-
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
-
- AC_CHECK_DECLS([snprintf, vsnprintf])
-
-
  dnl Cannot use AC_CHECK_FUNC because isinf may be a macro
  AC_CACHE_CHECK([for isinf], ac_cv_func_isinf,
  [AC_LINK_IFELSE([AC_LANG_PROGRAM([
--- 1613,1618 ----
*************** for the exact reason.]])],
*** 1829,1844 ****
  # Run tests below here
  # --------------------

- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_ARG_CONTROL
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
-
  dnl Check to see if we have a working 64-bit integer type.
  dnl Since Postgres 8.4, we no longer support compilers without a working
  dnl 64-bit type; but we have to determine whether that type is called
--- 1782,1787 ----
*************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in
*** 1861,1868 ****
    [Define to the name of a signed 64-bit integer type.])

  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 1804,1809 ----
*************** fi
*** 1872,1901 ****
  AC_DEFINE_UNQUOTED(INT64_MODIFIER, $INT64_MODIFIER,
                     [Define to the appropriate printf length modifier for 64-bit ints.])

- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_C99_RESULT
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
-
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-   AC_DEFINE(USE_REPL_SNPRINTF, 1, [Use replacement snprintf() functions.])
-   AC_LIBOBJ(snprintf)
- fi
-
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  PGAC_C_BUILTIN_OP_OVERFLOW
--- 1813,1818 ----
diff --git a/src/include/c.h b/src/include/c.h
index 13c794d..25d7d60 100644
*** a/src/include/c.h
--- b/src/include/c.h
*************** typedef union PGAlignedXLogBlock
*** 1147,1160 ****
   * standard C library.
   */

- #if !HAVE_DECL_SNPRINTF
- extern int    snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
- #endif
-
- #if !HAVE_DECL_VSNPRINTF
- extern int    vsnprintf(char *str, size_t count, const char *fmt, va_list args);
- #endif
-
  #if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC
  extern int    fdatasync(int fildes);
  #endif
--- 1147,1152 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 705c52b..254fa80 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 166,175 ****
     don't. */
  #undef HAVE_DECL_RTLD_NOW

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_SNPRINTF
-
  /* Define to 1 if you have the declaration of `strlcat', and to 0 if you
     don't. */
  #undef HAVE_DECL_STRLCAT
--- 166,171 ----
***************
*** 194,203 ****
     don't. */
  #undef HAVE_DECL_SYS_SIGLIST

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_VSNPRINTF
-
  /* Define to 1 if you have the `dlopen' function. */
  #undef HAVE_DLOPEN

--- 190,195 ----
***************
*** 507,515 ****
  /* Define to 1 if you have the `shm_open' function. */
  #undef HAVE_SHM_OPEN

- /* Define to 1 if you have the `snprintf' function. */
- #undef HAVE_SNPRINTF
-
  /* Define to 1 if you have spinlocks. */
  #undef HAVE_SPINLOCKS

--- 499,504 ----
***************
*** 712,720 ****
  /* Define to 1 if you have the <uuid/uuid.h> header file. */
  #undef HAVE_UUID_UUID_H

- /* Define to 1 if you have the `vsnprintf' function. */
- #undef HAVE_VSNPRINTF
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #undef HAVE_WCHAR_H

--- 701,706 ----
***************
*** 923,931 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  #undef USE_PAM

- /* Use replacement snprintf() functions. */
- #undef USE_REPL_SNPRINTF
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #undef USE_SLICING_BY_8_CRC32C

--- 909,914 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index eafe377..92d648e 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 135,144 ****
     don't. */
  #define HAVE_DECL_RTLD_NOW 0

- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_SNPRINTF 1
-
  /* Define to 1 if you have the declaration of `strnlen', and to 0 if you
     don't. */
  #define HAVE_DECL_STRNLEN 1
--- 135,140 ----
***************
*** 151,160 ****
     don't. */
  #define HAVE_DECL_STRTOULL 1

- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_VSNPRINTF 1
-
  /* Define to 1 if you have the `dlopen' function. */
  /* #undef HAVE_DLOPEN */

--- 147,152 ----
***************
*** 373,381 ****
  /* Define to 1 if you have the `setsid' function. */
  /* #undef HAVE_SETSID */

- /* Define to 1 if you have the `snprintf' function. */
- /* #undef HAVE_SNPRINTF */
-
  /* Define to 1 if you have spinlocks. */
  #define HAVE_SPINLOCKS 1

--- 365,370 ----
***************
*** 553,561 ****
  /* Define to 1 if you have the <utime.h> header file. */
  #define HAVE_UTIME_H 1

- /* Define to 1 if you have the `vsnprintf' function. */
- #define HAVE_VSNPRINTF 1
-
  /* Define to 1 if you have the <wchar.h> header file. */
  #define HAVE_WCHAR_H 1

--- 542,547 ----
***************
*** 712,720 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  /* #undef USE_PAM */

- /* Use replacement snprintf() functions. */
- #define USE_REPL_SNPRINTF 1
-
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #if (_MSC_VER < 1500)
  #define USE_SLICING_BY_8_CRC32C 1
--- 698,703 ----
diff --git a/src/include/port.h b/src/include/port.h
index cc2648c..522bd11 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern unsigned char pg_tolower(unsigned
*** 134,140 ****
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! #ifdef USE_REPL_SNPRINTF

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
--- 134,145 ----
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);

! /*
!  * Beginning in v12, we always replace snprintf() and friends with our own
!  * implementation.  This symbol is no longer consulted by the core code,
!  * but keep it defined anyway in case any extensions are looking at it.
!  */
! #define USE_REPL_SNPRINTF 1

  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
*************** extern int    pg_printf(const char *fmt,...
*** 187,193 ****
  #define fprintf            pg_fprintf
  #define printf            pg_printf
  #endif
- #endif                            /* USE_REPL_SNPRINTF */

  /* Replace strerror() with our own, somewhat more robust wrapper */
  extern char *pg_strerror(int errnum);
--- 192,197 ----
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index b7bd162..e07a7fa 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

--- 31,38 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= informix.o snprintf.o strerror.o \
!     $(filter strnlen.o, $(LIBOBJS)) $(WIN32RES)

  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes

diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index 005d25a..b381623 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,33 ****
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o strerror.o \
!     $(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
--- 26,33 ----
  LIBS := $(filter-out -lpgport, $(LIBS))

  OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
!     connect.o misc.o path.o pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
      $(WIN32RES)

  # thread.c is needed only for non-WIN32 implementation of path.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 18b2402..15d7f33 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,37 ****
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o strerror.o \
!     $(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

--- 30,37 ----
  SHLIB_EXPORTS = exports.txt

  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
!     pgstrcasecmp.o snprintf.o strerror.o \
!     $(filter rint.o strnlen.o, $(LIBOBJS)) \
      string.o \
      $(WIN32RES)

diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 8324f4f..a106088 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=    fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strlcpy.o strnlen.o
win32error.owin32setlocale.o, $(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
      libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
!     snprintf.o strerror.o thread.o
  # libpgport C files that are needed if identified by configure
! OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o strlcpy.o strnlen.o win32error.o
win32setlocale.o,$(LIBOBJS)) 

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index e6241f0..f8888a4 100644
*** a/src/pl/plperl/plperl.h
--- b/src/pl/plperl/plperl.h
***************
*** 29,39 ****
   * Sometimes perl carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  /*
   * ActivePerl 5.18 and later are MinGW-built, and their headers use GCC's
--- 29,36 ----
***************
*** 99,105 ****
  #endif

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 96,101 ----
***************
*** 113,119 ****
  #define vsnprintf        pg_vsnprintf
  #define snprintf        pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /* perl version and platform portability */
  #define NEED_eval_pv
--- 109,114 ----
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 6cc323a..aefbfc2 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 33,43 ****
   * Sometimes python carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
-
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif

  #if defined(_MSC_VER) && defined(_DEBUG)
  /* Python uses #pragma to bring in a non-default libpython on VC++ if
--- 33,40 ----
*************** typedef int Py_ssize_t;
*** 124,130 ****
  #include <eval.h>

  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 121,126 ----
*************** typedef int Py_ssize_t;
*** 138,144 ****
  #define vsnprintf                pg_vsnprintf
  #define snprintf                pg_snprintf
  #endif                            /* __GNUC__ */
- #endif                            /* USE_REPL_SNPRINTF */

  /*
   * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
--- 134,139 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index b3a10ba..a2ee8e2 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o sprompt.o strerror.o tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,40 ----
  OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
      noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
      pgstrcasecmp.o pqsignal.o \
!     qsort.o qsort_arg.o quotes.o snprintf.o sprompt.o strerror.o \
!     tar.o thread.o

  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/README b/src/port/README
index 4ae96da..c446b46 100644
*** a/src/port/README
--- b/src/port/README
*************** and adding infrastructure to recompile t
*** 18,24 ****

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter snprintf.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
--- 18,24 ----

          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter strlcat.o, $(LIBOBJS))

  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 798a823..df7e01f 100644
*** a/src/backend/lib/stringinfo.c
--- b/src/backend/lib/stringinfo.c
*************** resetStringInfo(StringInfo str)
*** 77,88 ****
--- 77,91 ----
  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);
*************** appendStringInfo(StringInfo str, const c
*** 105,110 ****
--- 108,116 ----
   * 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/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 7d1d439..ca73a55 100644
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** archputs(const char *s, Archive *AH)
*** 1507,1512 ****
--- 1507,1513 ----
  int
  archprintf(Archive *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** archprintf(Archive *AH, const char *fmt,
*** 1519,1524 ****
--- 1520,1526 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
*************** RestoreOutput(ArchiveHandle *AH, OutputC
*** 1640,1645 ****
--- 1642,1648 ----
  int
  ahprintf(ArchiveHandle *AH, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** ahprintf(ArchiveHandle *AH, const char *
*** 1652,1657 ****
--- 1655,1661 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 007be12..407a56d 100644
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
*************** _EndBlobs(ArchiveHandle *AH, TocEntry *t
*** 1026,1031 ****
--- 1026,1032 ----
  static int
  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *p;
      size_t        len = 128;        /* initial assumption about buffer size */
      size_t        cnt;
*************** tarPrintf(ArchiveHandle *AH, TAR_MEMBER
*** 1038,1043 ****
--- 1039,1045 ----
          p = (char *) pg_malloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          cnt = pvsnprintf(p, len, fmt, args);
          va_end(args);
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 04465a1..2cf100f 100644
*** a/src/common/psprintf.c
--- b/src/common/psprintf.c
***************
*** 45,50 ****
--- 45,51 ----
  char *
  psprintf(const char *fmt,...)
  {
+     int            save_errno = errno;
      size_t        len = 128;        /* initial assumption about buffer size */

      for (;;)
*************** psprintf(const char *fmt,...)
*** 60,65 ****
--- 61,67 ----
          result = (char *) palloc(len);

          /* Try to format the data. */
+         errno = save_errno;
          va_start(args, fmt);
          newlen = pvsnprintf(result, len, fmt, args);
          va_end(args);
*************** psprintf(const char *fmt,...)
*** 89,94 ****
--- 91,99 ----
   * Other error cases do not return, but exit via elog(ERROR) or exit().
   * Hence, this shouldn't be used inside libpq.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * 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.
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 0814ec6..43c36c3 100644
*** a/src/interfaces/libpq/pqexpbuffer.c
--- b/src/interfaces/libpq/pqexpbuffer.c
*************** enlargePQExpBuffer(PQExpBuffer str, size
*** 233,238 ****
--- 233,239 ----
  void
  printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** printfPQExpBuffer(PQExpBuffer str, const
*** 244,249 ****
--- 245,251 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 261,266 ****
--- 263,269 ----
  void
  appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+     int            save_errno = errno;
      va_list        args;
      bool        done;

*************** appendPQExpBuffer(PQExpBuffer str, const
*** 270,275 ****
--- 273,279 ----
      /* Loop in case we have to retry after enlarging the buffer. */
      do
      {
+         errno = save_errno;
          va_start(args, fmt);
          done = appendPQExpBufferVA(str, fmt, args);
          va_end(args);
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 281,286 ****
--- 285,293 ----
   * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
   * Attempt to format data and append it to str.  Returns true if done
   * (either successful or hard failure), false if need to retry.
+  *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
   */
  static bool
  appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index e244104..3814a6c 100644
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** static bool set_string_attr(PyObject *ob
*** 46,51 ****
--- 46,52 ----
  void
  PLy_elog_impl(int elevel, const char *fmt,...)
  {
+     int            save_errno = errno;
      char       *xmsg;
      char       *tbmsg;
      int            tb_depth;
*************** PLy_elog_impl(int elevel, const char *fm
*** 96,101 ****
--- 97,103 ----
              va_list        ap;
              int            needed;

+             errno = save_errno;
              va_start(ap, fmt);
              needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
              va_end(ap);
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 851e2ae..63cec5d 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 64,69 ****
--- 64,77 ----
   *
   * 5. Space and '#' flags are not implemented.
   *
+  * In addition, we support some extensions over C99:
+  *
+  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
+  *
+  * 2. "%m" expands to the value of strerror(errno), where errno is the
+  * value that variable had at the start of the call.  This is a glibc
+  * extension, but a very useful one.
+  *
   *
   * Historically the result values of sprintf/snprintf varied across platforms.
   * This implementation now follows the C99 standard:
*************** static void flushbuffer(PrintfTarget *ta
*** 155,160 ****
--- 163,175 ----
  static void dopr(PrintfTarget *target, const char *format, va_list args);


+ /*
+  * Externally visible entry points.
+  *
+  * All of these are just wrappers around dopr().  Note it's essential that
+  * they not change the value of "errno" before reaching dopr().
+  */
+
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** static void trailing_pad(int *padlen, Pr
*** 315,325 ****


  /*
!  * dopr(): poor man's version of doprintf
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
--- 330,341 ----


  /*
!  * dopr(): the guts of *printf for all cases.
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
+     int            save_errno = errno;
      const char *format_start = format;
      int            ch;
      bool        have_dollar;
*************** nextch1:
*** 497,502 ****
--- 513,519 ----
                  else
                      have_non_dollar = true;
                  break;
+             case 'm':
              case '%':
                  break;
          }
*************** nextch2:
*** 802,807 ****
--- 819,831 ----
                           precision, pointflag,
                           target);
                  break;
+             case 'm':
+                 {
+                     const char *errm = strerror(save_errno);
+
+                     dostr(errm, strlen(errm), target);
+                 }
+                 break;
              case '%':
                  dopr_outch('%', target);
                  break;
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 22e5d87..b9c1130 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void write_csvlog(ErrorData *edat
*** 177,183 ****
  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);
--- 177,182 ----
*************** errcode_for_socket_access(void)
*** 705,717 ****
   */
  #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); \
--- 704,713 ----
*************** errcode_for_socket_access(void)
*** 722,736 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 718,731 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, fmt); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** errcode_for_socket_access(void)
*** 746,760 ****
  #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); \
--- 741,752 ----
*************** errcode_for_socket_access(void)
*** 765,779 ****
          { \
              va_list        args; \
              int            needed; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmtbuf, 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); \
--- 757,770 ----
          { \
              va_list        args; \
              int            needed; \
+             errno = edata->saved_errno; \
              va_start(args, n); \
!             needed = appendStringInfoVA(&buf, fmt, args); \
              va_end(args); \
              if (needed == 0) \
                  break; \
              enlargeStringInfo(&buf, needed); \
          } \
          /* Save the completed message into the stack item */ \
          if (edata->targetfield) \
              pfree(edata->targetfield); \
*************** send_message_to_frontend(ErrorData *edat
*** 3329,3387 ****


  /*
-  * 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
--- 3320,3325 ----

Re: Allowing printf("%m") only where it actually works

От
Michael Paquier
Дата:
On Mon, Sep 24, 2018 at 01:18:47PM -0400, Tom Lane wrote:
> Michael Paquier <michael@paquier.xyz> writes:
>> On Wed, Sep 12, 2018 at 01:40:01PM -0400, Tom Lane wrote:
>>> Rebase attached --- no substantive changes.
>
>> -       if (handleDLL == NULL)
>> -           ereport(FATAL,
>> -                   (errmsg_internal("could not load netmsg.dll: error
>> -            code %lu", GetLastError())));
>
>> In 0001, this is replaced by a non-FATAL error for the backend, which
>> does not seem like a good idea to me because the user loses visibility
>> with this DDL which canot be loaded.  I still have to see this error...
>
> Well, we have to change the code somehow to make it usable in frontend
> as well as backend.  And we can *not* have it do exit(1) in libpq.
> So the solution I chose was to make it act the same as if FormatMessage
> were to fail.  I don't find this behavior unreasonable: what is really
> important is the original error code, not whether we were able to
> pretty-print it.  I think the ereport(FATAL) coding is a pretty darn
> bad idea even in the backend.

Ok.  I won't fight hard on that.  Why changing the error message from
"could not load netmsg.dll" to "unrecognized winsock error" then?  The
original error string is much more verbose to grab the context.

> Seems a bit make-worky, but here you go.  0001 is the same as before
> (but rebased up to today, so some line numbers change).  0002
> changes things so that we always use our snprintf, removing all the
> configure logic associated with that.  0003 implements %m in snprintf.c
> and adjusts our various printf-wrapper functions to ensure that they
> pass errno through reliably.  0004 changes elog.c to rely on %m being
> implemented below it.

Thanks for the new versions.  The only thing I could find to complain
about is the error message above, the rest looks in nice shape.
--
Michael

Вложения

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Michael Paquier <michael@paquier.xyz> writes:
> On Mon, Sep 24, 2018 at 01:18:47PM -0400, Tom Lane wrote:
>> Well, we have to change the code somehow to make it usable in frontend
>> as well as backend.  And we can *not* have it do exit(1) in libpq.
>> So the solution I chose was to make it act the same as if FormatMessage
>> were to fail.  I don't find this behavior unreasonable: what is really
>> important is the original error code, not whether we were able to
>> pretty-print it.  I think the ereport(FATAL) coding is a pretty darn
>> bad idea even in the backend.

> Ok.  I won't fight hard on that.  Why changing the error message from
> "could not load netmsg.dll" to "unrecognized winsock error" then?  The
> original error string is much more verbose to grab the context.

As the code stands, what you'll get told about is the error code
returned by the failed LoadLibrary call; the original winsock error
code is reported nowhere.  I think that's backwards.

We could possibly write something like

sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err, GetLastError())));

but I'm unconvinced that that's useful.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Alvaro Herrera
Дата:
On 2018-Sep-25, Tom Lane wrote:

> Michael Paquier <michael@paquier.xyz> writes:

> > Ok.  I won't fight hard on that.  Why changing the error message from
> > "could not load netmsg.dll" to "unrecognized winsock error" then?  The
> > original error string is much more verbose to grab the context.
> 
> As the code stands, what you'll get told about is the error code
> returned by the failed LoadLibrary call; the original winsock error
> code is reported nowhere.  I think that's backwards.

I agree that the winsock problem is the main one we should be reporting,
including its numeric error code.  Even if we can't translate it, the
numeric value can be translated by web-searching, if it comes to that.

> We could possibly write something like
> 
> sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err,
GetLastError())));
> 
> but I'm unconvinced that that's useful.

Actually I think it *is* useful to do it like this, because then the
user knows to fix the netmsg.dll problem so that they can continue to
investigate the winsock problem.  If we don't report the secondary error
message, how are users going to figure out how to fix the problem?

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> On 2018-Sep-25, Tom Lane wrote:
>> We could possibly write something like
>>
>> sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err,
GetLastError())));
>>
>> but I'm unconvinced that that's useful.

> Actually I think it *is* useful to do it like this, because then the
> user knows to fix the netmsg.dll problem so that they can continue to
> investigate the winsock problem.  If we don't report the secondary error
> message, how are users going to figure out how to fix the problem?

OK, I'm fine with doing it like that if people want it.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Michael Paquier
Дата:
On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote:
> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
>> Actually I think it *is* useful to do it like this, because then the
>> user knows to fix the netmsg.dll problem so that they can continue to
>> investigate the winsock problem.  If we don't report the secondary error
>> message, how are users going to figure out how to fix the problem?
>
> OK, I'm fine with doing it like that if people want it.

+1.
--
Michael

Вложения

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Michael Paquier <michael@paquier.xyz> writes:
> On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote:
>> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
>>> Actually I think it *is* useful to do it like this, because then the
>>> user knows to fix the netmsg.dll problem so that they can continue to
>>> investigate the winsock problem.  If we don't report the secondary error
>>> message, how are users going to figure out how to fix the problem?

>> OK, I'm fine with doing it like that if people want it.

> +1.

OK, pushed 0001 with that adjustment.

While looking over the thread, I remembered I wanted to convert
strerror_r into a wrapper as well.  Think I'll go do that next,
because really it'd be better for snprintf.c to be calling strerror_r
not strerror.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
I wrote:
> While looking over the thread, I remembered I wanted to convert
> strerror_r into a wrapper as well.  Think I'll go do that next,
> because really it'd be better for snprintf.c to be calling strerror_r
> not strerror.

So while chasing that, I realized that libpq contains its own version
of the backend's win32_socket_strerror code, in libpq/win32.c.
This probably explains why we've not heard complaints about bogus
socket error reports from libpq; it's that code that's handling it.

What I think ought to happen is to merge win32.c's version of that
code into strerror.c, which'd allow removing win32.c and win32.h
altogether.  However, not having a Windows environment, I can't
test such changes and probably shouldn't be the one to take point
on making the change.  Anybody?

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
Hi,

On 2018-09-24 13:18:47 -0400, Tom Lane wrote:
> 0002 changes things so that we always use our snprintf, removing all the
> configure logic associated with that.

In the commit message you wrote:

> Preliminary performance testing suggests that as it stands, snprintf.c is
> faster than the native printf functions for some tasks on some platforms,
> and slower for other cases.  A pending patch will improve that, though
> cases with floating-point conversions will doubtless remain slower unless
> we want to put a *lot* of effort into that.  Still, we've not observed
> that *printf is really a performance bottleneck for most workloads, so
> I doubt this matters much.

I severely doubt the last sentence. I've *many* times seen *printf be a
significant bottleneck. In particular just about any pg_dump of a
database that has large tables with even just a single float column is
commonly bottlenecked on float -> string conversion.

A trivial bad benchmark:

CREATE TABLE somefloats(id serial, data1 float8, data2 float8, data3 float8);
INSERT INTO somefloats(data1, data2, data3) SELECT random(), random(), random() FROM generate_series(1, 10000000);
VACUUM FREEZE somefloats;


postgres[12850][1]=# \dt+ somefloats
                       List of relations
┌────────┬────────────┬───────┬────────┬────────┬─────────────┐
│ Schema │    Name    │ Type  │ Owner  │  Size  │ Description │
├────────┼────────────┼───────┼────────┼────────┼─────────────┤
│ public │ somefloats │ table │ andres │ 575 MB │             │
└────────┴────────────┴───────┴────────┴────────┴─────────────┘

96bf88d52711ad3a0a4cc2d1d9cb0e2acab85e63:

COPY somefloats TO '/dev/null';
COPY 10000000
Time: 24575.770 ms (00:24.576)

96bf88d52711ad3a0a4cc2d1d9cb0e2acab85e63^:

COPY somefloats TO '/dev/null';
COPY 10000000
Time: 12877.037 ms (00:12.877)

IOW, we regress copy performance by about 2x. And one int and three
floats isn't a particularly insane table layout.


I'm not saying we shouldn't default to our printf - in fact I think we
probably past due to use a faster float->string conversion than we
portably get from the OS - but I don't think we can default to our
sprintf without doing something about the float conversion performance.


Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
On 2018-09-26 11:09:59 -0400, Tom Lane wrote:
> Michael Paquier <michael@paquier.xyz> writes:
> > On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote:
> >> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> >>> Actually I think it *is* useful to do it like this, because then the
> >>> user knows to fix the netmsg.dll problem so that they can continue to
> >>> investigate the winsock problem.  If we don't report the secondary error
> >>> message, how are users going to figure out how to fix the problem?
> 
> >> OK, I'm fine with doing it like that if people want it.
> 
> > +1.
> 
> OK, pushed 0001 with that adjustment.
> 
> While looking over the thread, I remembered I wanted to convert
> strerror_r into a wrapper as well.  Think I'll go do that next,
> because really it'd be better for snprintf.c to be calling strerror_r
> not strerror.

The strerror push, I assume it's that at least, broke something on icc:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16

================== pgsql.build/src/test/regress/regression.diffs ===================
*** /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/expected/create_function_1.out    Wed Sep 26 20:10:35
2018
--- /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/results/create_function_1.out    Wed Sep 26 20:10:43
2018
***************
*** 86,92 ****
  ERROR:  only one AS item needed for language "sql"
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
      AS 'nosuchfile';
! ERROR:  could not access file "nosuchfile": No such file or directory
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
      AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol';
  ERROR:  could not find function "nosuchsymbol" in file
"/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so"
--- 86,92 ----
  ERROR:  only one AS item needed for language "sql"
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
      AS 'nosuchfile';
! ERROR:  could not access file "nosuchfile": ENOENT
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
      AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol';
  ERROR:  could not find function "nosuchsymbol" in file
"/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so"

======================================================================


Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
On 2018-09-26 11:57:34 -0700, Andres Freund wrote:
> On 2018-09-26 11:09:59 -0400, Tom Lane wrote:
> > Michael Paquier <michael@paquier.xyz> writes:
> > > On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote:
> > >> Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> > >>> Actually I think it *is* useful to do it like this, because then the
> > >>> user knows to fix the netmsg.dll problem so that they can continue to
> > >>> investigate the winsock problem.  If we don't report the secondary error
> > >>> message, how are users going to figure out how to fix the problem?
> >
> > >> OK, I'm fine with doing it like that if people want it.
> >
> > > +1.
> >
> > OK, pushed 0001 with that adjustment.
> >
> > While looking over the thread, I remembered I wanted to convert
> > strerror_r into a wrapper as well.  Think I'll go do that next,
> > because really it'd be better for snprintf.c to be calling strerror_r
> > not strerror.
>
> The strerror push, I assume it's that at least, broke something on icc:
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16
>
> ================== pgsql.build/src/test/regress/regression.diffs ===================
> *** /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/expected/create_function_1.out    Wed Sep 26
20:10:352018 
> --- /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/results/create_function_1.out    Wed Sep 26
20:10:432018 
> ***************
> *** 86,92 ****
>   ERROR:  only one AS item needed for language "sql"
>   CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
>       AS 'nosuchfile';
> ! ERROR:  could not access file "nosuchfile": No such file or directory
>   CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
>       AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol';
>   ERROR:  could not find function "nosuchsymbol" in file
"/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so"
> --- 86,92 ----
>   ERROR:  only one AS item needed for language "sql"
>   CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
>       AS 'nosuchfile';
> ! ERROR:  could not access file "nosuchfile": ENOENT
>   CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C
>       AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol';
>   ERROR:  could not find function "nosuchsymbol" in file
"/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so"
>
> ======================================================================

Mandrill as well:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=mandrill&dt=2018-09-26%2018%3A30%3A26

*Lots* of new warnings like:
xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg
-DFRONTEND-I../../src/include -I/home/nm/sw/nopath/icu58.2-32/include -I/home/nm/sw/nopath/libxml2-32/include/libxml2
-I/home/nm/sw/nopath/uuid-32/include-I/home/nm/sw/nopath/openldap-32/include -I/home/nm/sw/nopath/icu58.2-32/include
-I/home/nm/sw/nopath/libxml2-32/include-DVAL_CONFIGURE="\"'--enable-cassert' '--enable-debug' '--enable-nls'
'--enable-tap-tests''--with-icu' '--with-ldap' '--with-libxml' '--with-libxslt' '--with-openssl' '--with-ossp-uuid'
'--with-perl''--with-python' '--with-includes=/home/nm/sw/nopath/uuid-32/include /home/nm/sw/nopath/openldap-32/include
/home/nm/sw/nopath/icu58.2-32/include/home/nm/sw/nopath/libxml2-32/include'
'--with-libraries=/home/nm/sw/nopath/uuid-32/lib/home/nm/sw/nopath/openldap-32/lib /home/nm/sw/nopath/icu58.2-32/lib
/home/nm/sw/nopath/libxml2-32/lib''--with-tcl' '--prefix=/home/nm/farm/xlc32/HEAD/inst' '--with-pgport=7678' 'CC=xlc_r
-qmaxmem=33554432-D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY'
'PKG_CONFIG_PATH=/home/nm/sw/nopath/icu58.2-32/lib/pkgconfig'\""-DVAL_CC="\"xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1
-DRANDOMIZE_ALLOCATED_MEMORY\""-DVAL_CPPFLAGS="\"-I/home/nm/sw/nopath/icu58.2-32/include
-I/home/nm/sw/nopath/libxml2-32/include/libxml2-I/home/nm/sw/nopath/uuid-32/include
-I/home/nm/sw/nopath/openldap-32/include-I/home/nm/sw/nopath/icu58.2-32/include
-I/home/nm/sw/nopath/libxml2-32/include\""-DVAL_CFLAGS="\"-qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg\""
-DVAL_CFLAGS_SL="\"\""-DVAL_LDFLAGS="\"-L/home/nm/sw/nopath/libxml2-32/lib -L/home/nm/sw/nopath/uuid-32/lib
-L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib
-Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib'\""
-DVAL_LDFLAGS_EX="\"\""-DVAL_LDFLAGS_SL="\" -Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE\"" -DVAL_LIBS="\"-lpgcommon -lpgport
-lintl-lxslt -lxml2 -lssl -lcrypto -lz -lreadline -lm \""  -c -o base64.o base64.c 
"../../src/include/common/fe_memutils.h", line 41.44: 1506-946 (W) Incorrect argument type specified for attribute
"format";this attribute is ignored. 
"../../src/include/common/fe_memutils.h", line 42.80: 1506-946 (W)
Incorrect argument type specified for attribute "format"; this attribute
is ignored.

and then fails with:
xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg
-D_REENTRANT-D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS  -o libpq.so.5 libpq.a -Wl,-bE:libpq.exp -L../../../src/port
-L../../../src/common  -L/home/nm/sw/nopath/libxml2-32/lib  -L/home/nm/sw/nopath/uuid-32/lib
-L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib
-Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib'
-Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE -lintl -lssl -lcrypto -lldap_r -llber -lpthreads 
ld: 0711-317 ERROR: Undefined symbol: ._isnan
ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information.
../../../src/Makefile.shlib:339: recipe for target 'libpq.so.5' failed

but I think the latter is more likely to be caused by
2e2a392de3 Wed Sep 26 08:25:24 2018 UTC  Fix problems in handling the line data type
rather than this thread.

Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
On 2018-09-26 12:09:34 -0700, Andres Freund wrote:
> and then fails with:
> xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg
-D_REENTRANT-D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS  -o libpq.so.5 libpq.a -Wl,-bE:libpq.exp -L../../../src/port
-L../../../src/common  -L/home/nm/sw/nopath/libxml2-32/lib  -L/home/nm/sw/nopath/uuid-32/lib
-L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib
-Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib'
-Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE -lintl -lssl -lcrypto -lldap_r -llber -lpthreads
 
> ld: 0711-317 ERROR: Undefined symbol: ._isnan
> ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information.
> ../../../src/Makefile.shlib:339: recipe for target 'libpq.so.5' failed
> 
> but I think the latter is more likely to be caused by
> 2e2a392de3 Wed Sep 26 08:25:24 2018 UTC  Fix problems in handling the line data type 
> rather than this thread.

Err, no, that commit was backend code, this fails in frontend code...

Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> The strerror push, I assume it's that at least, broke something on icc:
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16

Yeah.  It looks like the problem is that configure's test for strerror_r's
return type does not work on icc:

onfigure:10784: checking whether strerror_r returns int
configure:10805: icc -std=gnu99 -c  -mp1 -fno-strict-aliasing -g -O2 -pthread -D_REENTRANT -D_THREAD_SAFE
-D_POSIX_PTHREAD_SEMANTICS -D_GNU_SOURCE -I/usr/include/libxml2  conftest.c >&5 
conftest.c(45): warning #159: declaration is incompatible with previous "strerror_r" (declared at line 438 of
"/usr/include/string.h")
  int strerror_r(int, char *, size_t);
      ^

configure:10805: $? = 0
configure:10812: result: yes

Configure is expecting this to throw a hard error (if the platform
declares strerror_r to return char*) but icc only makes it a warning.

What I am not quite figuring out here is how the code seemed to work
before.  Before this patch, it only mattered in libpq because that was the
only place using pqStrerror.  Maybe the regression tests don't ever
produce a strerror output from libpq?

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> I'm not saying we shouldn't default to our printf - in fact I think we
> probably past due to use a faster float->string conversion than we
> portably get from the OS - but I don't think we can default to our
> sprintf without doing something about the float conversion performance.

Well, if you're unhappy about snprintf.c's performance, you could review
https://commitfest.postgresql.org/19/1763/
so I can push that.  In my tests, that got us down to circa 10% penalty
for float conversions.

More generally, I'm not averse to having our own float conversion code
if someone wants to put in the effort.  Performance aside, it'd be nice
to eliminate cross-platform differences in float output so we could get
rid of some of the Windows-specific regression result files.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
Hi,

On 2018-09-26 17:41:36 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > I'm not saying we shouldn't default to our printf - in fact I think we
> > probably past due to use a faster float->string conversion than we
> > portably get from the OS - but I don't think we can default to our
> > sprintf without doing something about the float conversion performance.
> 
> Well, if you're unhappy about snprintf.c's performance, you could review
> https://commitfest.postgresql.org/19/1763/
> so I can push that.  In my tests, that got us down to circa 10% penalty
> for float conversions.

Uh, I can do that, but the fact remains that your commit slowed down
COPY and other conversion intensive workloads by a *significant* amount.
I'm ok helping with improving/winning-back performance, but I do think
the obligation to do so remains with the committer/authors that caused a
performance regression.

Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2018-09-26 17:41:36 -0400, Tom Lane wrote:
>> Well, if you're unhappy about snprintf.c's performance, you could review
>> https://commitfest.postgresql.org/19/1763/
>> so I can push that.  In my tests, that got us down to circa 10% penalty
>> for float conversions.

> Uh, I can do that, but the fact remains that your commit slowed down
> COPY and other conversion intensive workloads by a *significant* amount.

[ shrug... ]  There are other cases that got faster (particularly after
the above-mentioned patch).  I do not wish to consider floating-point
conversion speed as the sole figure of merit for this change.  If we
are to consider only the worst-case, we should be reverting JIT.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Andres Freund
Дата:
On 2018-09-26 18:31:07 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > On 2018-09-26 17:41:36 -0400, Tom Lane wrote:
> >> Well, if you're unhappy about snprintf.c's performance, you could review
> >> https://commitfest.postgresql.org/19/1763/
> >> so I can push that.  In my tests, that got us down to circa 10% penalty
> >> for float conversions.
> 
> > Uh, I can do that, but the fact remains that your commit slowed down
> > COPY and other conversion intensive workloads by a *significant* amount.
> 
> [ shrug... ]  There are other cases that got faster (particularly after
> the above-mentioned patch).  I do not wish to consider floating-point
> conversion speed as the sole figure of merit for this change.  If we
> are to consider only the worst-case, we should be reverting JIT.

Oh, come on. One can be disabled with a GUC, has (although not good
enough) intelligence when it switches on, the other has ... none of
that.  Obviously performance is always a balancing act, but you'd be
pretty pissed at anybody else regressing performance in a non-fringe
case, and then refused responsibility.  And as I said, I'm willing to
help.

Greetings,

Andres Freund


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> Oh, come on. One can be disabled with a GUC, has (although not good
> enough) intelligence when it switches on, the other has ... none of
> that.  Obviously performance is always a balancing act, but you'd be
> pretty pissed at anybody else regressing performance in a non-fringe
> case, and then refused responsibility.  And as I said, I'm willing to
> help.

Well, fine, let's work on it.  Did you note Alexander's comparison to
some "stb" library in the other thread?  I wonder if we could borrow
code or at least ideas from that.

            regards, tom lane


Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
So, circling back to the very beginning of this thread where I worried
about all the compile warnings we get on NetBSD-current, I'm pleased
to report that HEAD compiles warning-free so long as you define
PG_PRINTF_ATTRIBUTE to "__syslog__" rather than "gnu_printf".

So attached is a proposed patch to make configure check whether %m
works without a warning, and try "__syslog__" if "gnu_printf" does
not work for that.  (I did it in that order so that if NetBSD get
their heads screwed back on straight and stop complaining about
perfectly GNU-compliant code, we'll go back to selecting "gnu_printf".)

Any objections?

            regards, tom lane

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index fb58c94..af2dea1 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 21,41 ****
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
  # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
! [extern int
! pgac_write(int ignore, const char *fmt,...)
! __attribute__((format(gnu_printf, 2, 3)));], [])],
!                   [pgac_cv_printf_archetype=gnu_printf],
!                   [pgac_cv_printf_archetype=printf])
! ac_c_werror_flag=$ac_save_c_werror_flag])
! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
!                    [Define to gnu_printf if compiler supports it, else printf.])
! ])# PGAC_PRINTF_ARCHETYPE


  # PGAC_TYPE_64BIT_INT(TYPE)
--- 21,56 ----
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
  # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).  However,
! # on some NetBSD versions, that doesn't work while "__syslog__" does.
! # If all else fails, use "printf".
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
+ [pgac_cv_printf_archetype=gnu_printf
+ PGAC_TEST_PRINTF_ARCHETYPE
+ if [[ "$ac_archetype_ok" = no ]]; then
+   pgac_cv_printf_archetype=__syslog__
+   PGAC_TEST_PRINTF_ARCHETYPE
+   if [[ "$ac_archetype_ok" = no ]]; then
+     pgac_cv_printf_archetype=printf
+   fi
+ fi])
+ AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
+ [Define to best printf format archetype, usually gnu_printf if available.])
+ ])# PGAC_PRINTF_ARCHETYPE
+
+ # Subroutine: test $pgac_cv_printf_archetype, set $ac_archetype_ok to yes or no
+ AC_DEFUN([PGAC_TEST_PRINTF_ARCHETYPE],
  [ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
! [extern void pgac_write(int ignore, const char *fmt,...)
! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));],
! [pgac_write(0, "error %s: %m", "foo");])],
!                   [ac_archetype_ok=yes],
!                   [ac_archetype_ok=no])
! ac_c_werror_flag=$ac_save_c_werror_flag
! ])# PGAC_TEST_PRINTF_ARCHETYPE


  # PGAC_TYPE_64BIT_INT(TYPE)
diff --git a/configure b/configure
index 0448c6b..b7250d7 100755
*** a/configure
--- b/configure
*************** $as_echo_n "checking for printf format a
*** 13583,13610 ****
  if ${pgac_cv_printf_archetype+:} false; then :
    $as_echo_n "(cached) " >&6
  else
!   ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
  /* end confdefs.h.  */
! extern int
! pgac_write(int ignore, const char *fmt,...)
! __attribute__((format(gnu_printf, 2, 3)));
  int
  main ()
  {

    ;
    return 0;
  }
  _ACEOF
  if ac_fn_c_try_compile "$LINENO"; then :
!   pgac_cv_printf_archetype=gnu_printf
  else
!   pgac_cv_printf_archetype=printf
  fi
  rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
  ac_c_werror_flag=$ac_save_c_werror_flag
  fi
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5
  $as_echo "$pgac_cv_printf_archetype" >&6; }
--- 13583,13639 ----
  if ${pgac_cv_printf_archetype+:} false; then :
    $as_echo_n "(cached) " >&6
  else
!   pgac_cv_printf_archetype=gnu_printf
! ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
  /* end confdefs.h.  */
! extern void pgac_write(int ignore, const char *fmt,...)
! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));
  int
  main ()
  {
+ pgac_write(0, "error %s: %m", "foo");
+   ;
+   return 0;
+ }
+ _ACEOF
+ if ac_fn_c_try_compile "$LINENO"; then :
+   ac_archetype_ok=yes
+ else
+   ac_archetype_ok=no
+ fi
+ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag

+ if [ "$ac_archetype_ok" = no ]; then
+   pgac_cv_printf_archetype=__syslog__
+   ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+ extern void pgac_write(int ignore, const char *fmt,...)
+ __attribute__((format($pgac_cv_printf_archetype, 2, 3)));
+ int
+ main ()
+ {
+ pgac_write(0, "error %s: %m", "foo");
    ;
    return 0;
  }
  _ACEOF
  if ac_fn_c_try_compile "$LINENO"; then :
!   ac_archetype_ok=yes
  else
!   ac_archetype_ok=no
  fi
  rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
  ac_c_werror_flag=$ac_save_c_werror_flag
+
+   if [ "$ac_archetype_ok" = no ]; then
+     pgac_cv_printf_archetype=printf
+   fi
+ fi
  fi
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5
  $as_echo "$pgac_cv_printf_archetype" >&6; }
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 7894caa..9798bd2 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 804,810 ****
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define to gnu_printf if compiler supports it, else printf. */
  #undef PG_PRINTF_ATTRIBUTE

  /* PostgreSQL version as a string */
--- 804,810 ----
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define to best printf format archetype, usually gnu_printf if available. */
  #undef PG_PRINTF_ATTRIBUTE

  /* PostgreSQL version as a string */

Re: Allowing printf("%m") only where it actually works

От
Tom Lane
Дата:
So, circling back to the very beginning of this thread where I worried
about all the compile warnings we get on NetBSD-current, I'm pleased
to report that HEAD compiles warning-free so long as you define
PG_PRINTF_ATTRIBUTE to "__syslog__" rather than "gnu_printf".

So attached is a proposed patch to make configure check whether %m
works without a warning, and try "__syslog__" if "gnu_printf" does
not work for that.  (I did it in that order so that if NetBSD get
their heads screwed back on straight and stop complaining about
perfectly GNU-compliant code, we'll go back to selecting "gnu_printf".)

Any objections?

            regards, tom lane

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index fb58c94..af2dea1 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 21,41 ****
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
  # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
! [extern int
! pgac_write(int ignore, const char *fmt,...)
! __attribute__((format(gnu_printf, 2, 3)));], [])],
!                   [pgac_cv_printf_archetype=gnu_printf],
!                   [pgac_cv_printf_archetype=printf])
! ac_c_werror_flag=$ac_save_c_werror_flag])
! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
!                    [Define to gnu_printf if compiler supports it, else printf.])
! ])# PGAC_PRINTF_ARCHETYPE


  # PGAC_TYPE_64BIT_INT(TYPE)
--- 21,56 ----
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
  # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).  However,
! # on some NetBSD versions, that doesn't work while "__syslog__" does.
! # If all else fails, use "printf".
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
+ [pgac_cv_printf_archetype=gnu_printf
+ PGAC_TEST_PRINTF_ARCHETYPE
+ if [[ "$ac_archetype_ok" = no ]]; then
+   pgac_cv_printf_archetype=__syslog__
+   PGAC_TEST_PRINTF_ARCHETYPE
+   if [[ "$ac_archetype_ok" = no ]]; then
+     pgac_cv_printf_archetype=printf
+   fi
+ fi])
+ AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
+ [Define to best printf format archetype, usually gnu_printf if available.])
+ ])# PGAC_PRINTF_ARCHETYPE
+
+ # Subroutine: test $pgac_cv_printf_archetype, set $ac_archetype_ok to yes or no
+ AC_DEFUN([PGAC_TEST_PRINTF_ARCHETYPE],
  [ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
! [extern void pgac_write(int ignore, const char *fmt,...)
! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));],
! [pgac_write(0, "error %s: %m", "foo");])],
!                   [ac_archetype_ok=yes],
!                   [ac_archetype_ok=no])
! ac_c_werror_flag=$ac_save_c_werror_flag
! ])# PGAC_TEST_PRINTF_ARCHETYPE


  # PGAC_TYPE_64BIT_INT(TYPE)
diff --git a/configure b/configure
index 0448c6b..b7250d7 100755
*** a/configure
--- b/configure
*************** $as_echo_n "checking for printf format a
*** 13583,13610 ****
  if ${pgac_cv_printf_archetype+:} false; then :
    $as_echo_n "(cached) " >&6
  else
!   ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
  /* end confdefs.h.  */
! extern int
! pgac_write(int ignore, const char *fmt,...)
! __attribute__((format(gnu_printf, 2, 3)));
  int
  main ()
  {

    ;
    return 0;
  }
  _ACEOF
  if ac_fn_c_try_compile "$LINENO"; then :
!   pgac_cv_printf_archetype=gnu_printf
  else
!   pgac_cv_printf_archetype=printf
  fi
  rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
  ac_c_werror_flag=$ac_save_c_werror_flag
  fi
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5
  $as_echo "$pgac_cv_printf_archetype" >&6; }
--- 13583,13639 ----
  if ${pgac_cv_printf_archetype+:} false; then :
    $as_echo_n "(cached) " >&6
  else
!   pgac_cv_printf_archetype=gnu_printf
! ac_save_c_werror_flag=$ac_c_werror_flag
  ac_c_werror_flag=yes
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
  /* end confdefs.h.  */
! extern void pgac_write(int ignore, const char *fmt,...)
! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));
  int
  main ()
  {
+ pgac_write(0, "error %s: %m", "foo");
+   ;
+   return 0;
+ }
+ _ACEOF
+ if ac_fn_c_try_compile "$LINENO"; then :
+   ac_archetype_ok=yes
+ else
+   ac_archetype_ok=no
+ fi
+ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag

+ if [ "$ac_archetype_ok" = no ]; then
+   pgac_cv_printf_archetype=__syslog__
+   ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+ extern void pgac_write(int ignore, const char *fmt,...)
+ __attribute__((format($pgac_cv_printf_archetype, 2, 3)));
+ int
+ main ()
+ {
+ pgac_write(0, "error %s: %m", "foo");
    ;
    return 0;
  }
  _ACEOF
  if ac_fn_c_try_compile "$LINENO"; then :
!   ac_archetype_ok=yes
  else
!   ac_archetype_ok=no
  fi
  rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
  ac_c_werror_flag=$ac_save_c_werror_flag
+
+   if [ "$ac_archetype_ok" = no ]; then
+     pgac_cv_printf_archetype=printf
+   fi
+ fi
  fi
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5
  $as_echo "$pgac_cv_printf_archetype" >&6; }
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 7894caa..9798bd2 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 804,810 ****
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define to gnu_printf if compiler supports it, else printf. */
  #undef PG_PRINTF_ATTRIBUTE

  /* PostgreSQL version as a string */
--- 804,810 ----
  /* PostgreSQL major version as a string */
  #undef PG_MAJORVERSION

! /* Define to best printf format archetype, usually gnu_printf if available. */
  #undef PG_PRINTF_ATTRIBUTE

  /* PostgreSQL version as a string */