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: