Re: BUG #15511: Drop table error "invalid argument" - Mailing list pgsql-bugs

From Tom Lane
Subject Re: BUG #15511: Drop table error "invalid argument"
Date
Msg-id 3314.1544139501@sss.pgh.pa.us
Whole thread Raw
In response to Re: BUG #15511: Drop table error "invalid argument"  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: BUG #15511: Drop table error "invalid argument"
List pgsql-bugs
... so my preliminary result is that there's a *boatload* of "invalid"
format strings in our translation files, all of them apparently with
the extra-space disease.  See attached.

While it looks like every one of these is a typo, I'm beginning to
have second thoughts about the premise here.  Presumably, the reason
we've not noticed this issue before is that almost all non-Windows
platforms were using libc's printf, which is going to implement the
full set of POSIX-specified flags including space.  As of HEAD that
is no longer the case, because we use snprintf.c everywhere.  While
it might not be a problem for our own code if snprintf.c is a couple
of flags short of full POSIX, I'm worrying that third-party code
might be unhappy if *printf in the PG environment behaves oddly.

So maybe the best compromise is to upgrade snprintf.c.  I think it'd
only take a few more lines to implement the space flag per spec.
The other flags that we aren't implementing are '#' ("alternate form")
and "'" (thousands grouping).  For those, I'd be a bit inclined to accept
them but treat 'em as no-ops.  On the other hand, that still leaves
some daylight between us and POSIX, in the form of stuff like "long
double" support, and I still don't want to go near that.

Thoughts?

For the archives' sake, I attach the code I used for this.
I'm now thinking maybe we wouldn't commit it, though.

            regards, tom lane

invalid format string in file "src/bin/initdb/po/he.po" for ../../common/restricted_token.c:77
"תכנית %s: לא ניתן לפתוח את התהליך token: קוד שגיאה % lu
"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/restricted_token.c:90
"תכנית %s: לא ניתן להקצות SID: קוד שגיאה % lu
"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/restricted_token.c:132
"תכנית %s:  לא ניתן להפעיל תהליך עבור הפקודה "%s": קוד שגיאה % lu
"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/restricted_token.c:170
"תכנית %s:  לא ניתן לבצע מחדש עם אסימון גישה מוגבל: קוד שגיאה % lu
"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/restricted_token.c:186
"תכנית %s:  לא ניתן לקבל קוד היציאה מן תהליך משנה: קוד שגיאה % lu
"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/username.c:43
"לא יכול לחפש יעיל את המשתמש עם מזהה % ld: %s"
invalid format string in file "src/bin/initdb/po/he.po" for ../../common/username.c:60
"כישלון בדיקה עבור שם המשתמש: קוד שגיאה % lu"
invalid format string in file "src/bin/initdb/po/tr.po" for initdb.c:2171
"% s: geçersiz yerel ayarlar; LANG ve LC_ * ortam değişkenlerini denetleyin.
"
invalid format string in file "src/bin/initdb/po/tr.po" for initdb.c:3195
"% s: superuser adı "% s" izin verilmiyor; rol adları "pg_" ile başlayamaz
"
invalid format string in file "src/bin/scripts/po/he.po" for ../../common/username.c:43
"לא יכול לחפש יעיל את המשתמש עם מזהה % ld: %s"
invalid format string in file "src/bin/scripts/po/he.po" for ../../common/username.c:60
"כישלון בדיקה עבור שם המשתמש: קוד שגיאה % lu"
invalid format string in file "src/bin/scripts/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורה)"
invalid format string in file "src/bin/scripts/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורה)"
invalid format string in file "src/bin/scripts/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורות)"
invalid format string in file "src/bin/psql/po/he.po" for ../../common/username.c:43
"לא יכול לחפש יעיל את המשתמש עם מזהה % ld: %s"
invalid format string in file "src/bin/psql/po/he.po" for ../../common/username.c:60
"כישלון בדיקה עבור שם המשתמש: קוד שגיאה % lu"
invalid format string in file "src/bin/psql/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורה)"
invalid format string in file "src/bin/psql/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורה)"
invalid format string in file "src/bin/psql/po/he.po" for ../../fe_utils/print.c:353
"(% lu שורות)"
invalid format string in file "src/bin/psql/po/he.po" for command.c:553
"לא ניתן לקבל את ספריית הבית עבור משתמש עם מזהה % ld: %s
"
invalid format string in file "src/bin/psql/po/he.po" for common.c:608
"זמן: %.3f ms (% 02d:%06.3f)
"
invalid format string in file "src/bin/psql/po/he.po" for common.c:617
"זמן: %.3f ms (% 02d:%02d:%06.3f)
"
invalid format string in file "src/bin/pg_basebackup/po/he.po" for pg_basebackup.c:965
"תכנתי %s: לא היתה אפשרות להגדיר את רמת הדחיסה % d: %s
"
invalid format string in file "src/bin/pg_basebackup/po/he.po" for pg_recvlogical.c:135
"תכנית %s: מאשר לכתוב עד % X/%X, סומק ל %X/%X (חריץ %s)
"
invalid format string in file "src/bin/pg_basebackup/po/he.po" for receivelog.c:278
"תכנית %s: שרת דיווח שם קובץ היסטוריה לא צפוי עבור ציר הזמן % u: %s
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:495
"תכנית %s:  לא היתה אפשרות להפעיל שרת: קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:967
"תכנית %s: לא יכול להפסיק את השרת; מופעל שרת למשתמש יחיד (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:975
"תכנית %s: לא היתה אפשרות לשלוח אות עצירה (PID: % ld): %s
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1058
"תכנית %s: לא ניתן להפעיל מחדש את השרת; מופעל שרת למשתמש יחיד (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1120
"תכנית %s: תהליך השרת הישן (PID: % ld) כנראה פועל
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1143
"תכנית %s: לא ניתן להפעיל מחדש את השרת; מופעל שרת למשתמש יחיד (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1152
"תכנית %s: לא היתה אפשרות לשלוח אות להפעלה מחדש (PID: % ld): %s
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1182
"תכנית %s: לא ניתן לקדם שרת; מופעל שרת למשתמש יחיד (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1219
"תכנית %s: לא היתה אפשרות לשלוח את האות  לקדם(PID: % ld): %s
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1301
"תכנית %s: שרת משתמש יחיד  פועל (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1314
"תכנית %s: שרת פועל (PID: % ld)
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1348
"תכנית %s: לא היתה אפשרות לשלוח אות %d (PID: % ld): %s
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1502
"תכנית %s: אין אפשרות לרשום את השירות "%s": קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1532
"תכנית %s: לא היתה אפשרות לפתוח שירות '%s': קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1541
"תכנית %s: לא יכול לבטל את השירות "%s": קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1690
"תכנית %s: לא היתה אפשרות להפעיל שירות '%s': קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1777
"תכנית %s: לא ניתן לפתוח את התהליך token: קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_ctl/po/he.po" for pg_ctl.c:1791
"תכנית %s: לא ניתן להקצות SID: קוד שגיאה % lu
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1302
"לא היתה אפשרות ליצור אובייקט גדול % u: %s"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1307
"לא היתה אפשרות לפתוח את אובייקט גדול % u: %s"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1652
"נכתב % lu בית של נתוני האובייקט הגדול (תוצאה = % lu)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1652
"נכתב % lu בית של נתוני האובייקט הגדול (תוצאה = % lu)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1652
"נכתבו % lu בתים של נתוני האובייקט הגדול (תוצאה = % lu)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:1658
"לא מצליח לכתוב על אובייקט גדול (תוצאה: % lu, צפוי: % lu)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:2147
"קובץ הקלט קצר מדי (לקרוא % lu, מצופה 5)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:2562
"מזהה כניסה% d מחוץ לטווח - אולי TOC מושחת
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:3659
"גירסה לא נתמכת (% d.%d) בכותרת הקובץ
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_archiver.c:3664
"בדיקת שפיות על גודל מספר שלם (% lu) נכשלה
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_custom.c:451
"סוג בלוק נתונים לא מזוהה (% d) בעת חיפוש בארכיון
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_custom.c:472
"לא יכול למצוא את מזהה הבלוק % d בארכיון - אולי בשל בקשת שחזור שאינה מסודרת, שלא ניתן לטפל בה עקב חוסר קיזוז נתונים
בארכיון
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_custom.c:477
"לא יכול למצוא את מזהה הבלוק% d בארכיון - אולי בשל בקשת שחזור שאינה מסודרת, שלא ניתן לטפל בה עקב קובץ קלט שאינו ניתן
לחיפושבו 
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_custom.c:482
"לא יכול למצוא את מזהה הבלוק% d בארכיון - אולי בשל בקשת שחזור שאינה מסודרת, שלא ניתן לטפל בה עקב קובץ ארכיון מושחת
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_tar.c:1237
"כותרת tar לא שלמה נמצאה (% lu בית)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_tar.c:1237
"כותרת tar לא שלמה נמצאה (% lu בית)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_backup_tar.c:1237
"כותרת tar לא שלמה נמצאה (% lu בתים)
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_dump.c:1000
"צוין קידוד לקוח לא חוקי "% s"
"
invalid format string in file "src/bin/pg_dump/po/he.po" for pg_dump.c:3095
"שגיאה בעת קריאת אובייקט גדול % u: %s"
invalid format string in file "src/backend/po/id.po" for commands/vacuumlazy.c:1093
"% .0f mati versi baris tidak dapat dihapus belum.
Ada % .0f item pointer yang tidak terpakai.
halaman%u sepenuhnya kosong.
%s."
invalid format string in file "src/backend/po/id.po" for executor/functions.c:225
"tidak bisa menentukan pernyataan tipe argumen yang sebenarnya % s"
invalid format string in file "src/backend/po/id.po" for executor/functions.c:638
"tidak bisa menentukan hasil yang sebenarnya untuk pernyataan kepada pengembalian tipe % s"
invalid format string in file "src/backend/po/id.po" for libpq/auth.c:2203
"tidak dapat melakukan pengumpulan inisialisasi LDAP untuk ldapbinddn « %s » pada server « %s »:% s"
invalid format string in file "src/backend/po/id.po" for libpq/auth.c:2283
"tidak dapat melepaskan setelah mencari pengguna « %s » di server « %s »:% s"
invalid format string in file "src/backend/po/fr.po" for catalog/objectaddress.c:1903
"le droit par défaut pour l'utilisateur « % s» dans le schéma « %s » de %s n'existe pas"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-auth.c:1045
"כישלון בדיקה עבור שם המשתמש: קוד שגיאה % lu
"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-auth.c:1055
"לא יכול לחפש משתמש מקומי עם מזהה % d: %s
"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-connect.c:1561
"נכשל WSAIoctl(SIO_KEEPALIVE_VALS): % ui
"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-misc.c:292
"מספר שלם בגודל % lu אינו נתמך על ידי pqGetInt"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-misc.c:328
"מספר שלם בגודל % lu אינו נתמך על ידי pqPutInt"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-protocol2.c:529
"תו לא צפוי% c בעקבות תגובת שאילתה ריקה (הודעת "I")"
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-protocol3.c:1316
"השורה % d: "
invalid format string in file "src/interfaces/libpq/po/he.po" for fe-secure-openssl.c:1465
"קוד שגיאת SSL % lu"
invalid format string in file "src/pl/plpython/po/vi.po" for plpy_typeio.c:1541
"thuộc tính "% s" không tồn tại trong đối tượng Python"
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index a773381..42aaea8 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 110,115 ****
--- 110,129 ----
  #undef    printf

  /*
+  * If this file is compiled with SNPRINTF_VALIDATE defined, it doesn't
+  * provide any of the normal *printf functions, but instead exports
+  * a function "pg_validate_printf_format()" that just detects whether a
+  * supplied format string is syntactically valid.  We implement that
+  * by running the normal snprintf code with a fake output buffer, and
+  * suppressing all attempts to fetch variable arguments (since those
+  * won't be there).
+  */
+ #ifdef SNPRINTF_VALIDATE
+ #undef va_arg
+ #define va_arg(args, type)  ((type) 0)
+ #endif
+
+ /*
   * Info about where the formatted output is going.
   *
   * dopr and subroutines will not write at/past bufend, but snprintf
*************** static void dopr(PrintfTarget *target, c
*** 170,175 ****
--- 184,191 ----
   * they not change the value of "errno" before reaching dopr().
   */

+ #ifndef SNPRINTF_VALIDATE
+
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** pg_printf(const char *fmt,...)
*** 290,295 ****
--- 306,335 ----
      return len;
  }

+ #else                            /* SNPRINTF_VALIDATE */
+
+ /*
+  * Returns 1 if "fmt" is syntactically valid, -1 if not.
+  * In the latter case, errno is set (but it'll always be EINVAL).
+  */
+ int
+ pg_validate_printf_format(const char *fmt)
+ {
+     /* Code is basically the same as pg_vsnprintf with NULL str argument */
+     PrintfTarget target;
+     char        onebyte[1];
+
+     target.bufstart = target.bufptr = onebyte;
+     target.bufend = onebyte;
+     target.stream = NULL;
+     target.nchars = 0;
+     target.failed = false;
+     dopr(&target, fmt, NULL);
+     return target.failed ? -1 : 1;
+ }
+
+ #endif                            /* SNPRINTF_VALIDATE */
+
  /*
   * Attempt to write the entire buffer to target->stream; discard the entire
   * buffer in any case.  Call this only when target->stream is defined.
*************** dopr(PrintfTarget *target, const char *f
*** 438,445 ****
--- 478,489 ----
          if (*format == 's')
          {
              format++;
+ #ifndef SNPRINTF_VALIDATE
              strvalue = va_arg(args, char *);
              Assert(strvalue != NULL);
+ #else
+             strvalue = "";
+ #endif
              dostr(strvalue, strlen(strvalue), target);
              if (target->failed)
                  break;
*************** nextch2:
*** 666,677 ****
--- 710,726 ----
                      else
                          fieldwidth = accum;
                  }
+ #ifndef SNPRINTF_VALIDATE
                  if (have_dollar)
                      strvalue = argvalues[fmtpos].cptr;
                  else
                      strvalue = va_arg(args, char *);
                  /* Whine if someone tries to print a NULL string */
                  Assert(strvalue != NULL);
+ #else
+                 /* va_arg would return NULL, so inject empty string instead */
+                 strvalue = "";
+ #endif
                  fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
                         target);
                  break;
*************** fail:
*** 1252,1257 ****
--- 1301,1308 ----
      target->failed = true;
  }

+ #ifndef SNPRINTF_VALIDATE
+
  /*
   * Nonstandard entry point to print a double value efficiently.
   *
*************** fail:
*** 1352,1357 ****
--- 1403,1410 ----
                                   + target.nchars);
  }

+ #endif                            /* SNPRINTF_VALIDATE */
+

  static void
  dostr(const char *str, int slen, PrintfTarget *target)
diff --git a/src/port/test_po_file.c b/src/port/test_po_file.c
index ...8dc063c .
*** a/src/port/test_po_file.c
--- b/src/port/test_po_file.c
***************
*** 0 ****
--- 1,150 ----
+ /*-------------------------------------------------------------------------
+  *
+  * test_po_file.c
+  *      Test messages in a .po file to see if our version of snprintf likes them.
+  *
+  * Copyright (c) 2018, PostgreSQL Global Development Group
+  *
+  *
+  * IDENTIFICATION
+  *      src/port/test_po_file.c
+  *
+  *-------------------------------------------------------------------------
+  */
+
+ #include "c.h"
+
+ #include <gettext-po.h>
+
+ /* snprintf.c will provide this below */
+ extern int    pg_validate_printf_format(const char *fmt);
+
+
+ /* Error handlers required by recent libgettextpo versions */
+ static void
+ xerror(int severity,
+        po_message_t message,
+        const char *filename, size_t lineno, size_t column,
+        int multiline_p, const char *message_text)
+ {
+     fprintf(stderr, "PO error: %s\n", message_text);
+     if (filename)
+         fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+                 filename, (int) lineno);
+     if (severity == PO_SEVERITY_FATAL_ERROR)
+         exit(1);
+ }
+
+ static void
+ xerror2(int severity,
+         po_message_t message1,
+         const char *filename1, size_t lineno1, size_t column1,
+         int multiline_p1, const char *message_text1,
+         po_message_t message2,
+         const char *filename2, size_t lineno2, size_t column2,
+         int multiline_p2, const char *message_text2)
+ {
+     fprintf(stderr, "PO error: %s ...\n", message_text1);
+     if (filename1)
+         fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+                 filename1, (int) lineno1);
+     fprintf(stderr, "PO error: ... %s\n", message_text2);
+     if (filename2)
+         fprintf(stderr, "Error occurred in file \"%s\" at line %d:\n",
+                 filename2, (int) lineno2);
+     if (severity == PO_SEVERITY_FATAL_ERROR)
+         exit(1);
+ }
+
+ static const struct po_xerror_handler handler = {
+     xerror, xerror2
+ };
+
+
+ /*
+  * Main program.
+  *
+  * Test each .po file named on the command line.
+  */
+ int
+ main(int argc, char **argv)
+ {
+     for (int i = 1; i < argc; i++)
+     {
+         const char *filename = argv[i];
+         po_file_t    file = po_file_read(filename, &handler);
+         const char *const *domains;
+         const char *const *domainp;
+
+         if (file == NULL)
+         {
+             fprintf(stderr, "could not open file \"%s\": %s\n",
+                     filename, strerror(errno));
+             exit(1);
+         }
+
+         domains = po_file_domains(file);
+         for (domainp = domains; *domainp; domainp++)
+         {
+             const char *domain = *domainp;
+             po_message_iterator_t iterator = po_message_iterator(file, domain);
+
+             for (;;)
+             {
+                 po_message_t message = po_next_message(iterator);
+                 po_filepos_t pos;
+                 char        location[256];
+                 const char *fmt;
+
+                 if (message == NULL)
+                     break;
+
+                 if (!po_message_is_format(message, "c-format"))
+                     continue;
+
+                 pos = po_message_filepos(message, 0);
+                 if (pos)
+                     snprintf(location, sizeof(location), "for %s:%ld",
+                              po_filepos_file(pos),
+                              (long) po_filepos_start_line(pos));
+                 else
+                     strcpy(location, "unknown location");
+
+                 fmt = po_message_msgid(message);
+                 if (fmt && pg_validate_printf_format(fmt) < 0)
+                     fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+                             filename, location, fmt);
+                 fmt = po_message_msgid_plural(message);
+                 if (fmt && pg_validate_printf_format(fmt) < 0)
+                     fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+                             filename, location, fmt);
+                 fmt = po_message_msgstr(message);
+                 if (fmt && pg_validate_printf_format(fmt) < 0)
+                     fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+                             filename, location, fmt);
+                 for (int ndx = 0;; ndx++)
+                 {
+                     fmt = po_message_msgstr_plural(message, ndx);
+                     if (!fmt)
+                         break;
+                     if (pg_validate_printf_format(fmt) < 0)
+                         fprintf(stderr, "invalid format string in file \"%s\" %s\n\"%s\"\n",
+                                 filename, location, fmt);
+                 }
+             }
+             po_message_iterator_free(iterator);
+         }
+         po_file_free(file);
+     }
+
+     return 0;
+ }
+
+ /*
+  * Now include snprintf.c with the desired variant compilation flag.
+  * We put this at the end so that its messing about with #define's
+  * won't mess up code in this file.
+  */
+ #define SNPRINTF_VALIDATE 1
+
+ #include "snprintf.c"

pgsql-bugs by date:

Previous
From: Tom Lane
Date:
Subject: Re: BUG #15511: Drop table error "invalid argument"
Next
From: PG Bug reporting form
Date:
Subject: BUG #15538: Postgres query performance is slow.