From 92459e7a7f87f91fc3012bea9eef870cf464d91f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 4 Jan 2014 16:05:16 -0500 Subject: [PATCH] Fix translatability markings in psql, and add defenses against future bugs. Several previous commits have added columns to various \d queries without updating their translate_columns[] arrays, leading to potentially incorrect translations in NLS-enabled builds. Offenders include commit 893686762 (added prosecdef to \df+), c9ac00e6e (added description to \dc+) and 3b17efdfd (added description to \dC+). Fix those cases back to 9.3 or 9.2 as appropriate. Since this is evidently more easily missed than one would like, in HEAD also add an Assert that the supplied array is long enough. This requires an API change for printQuery(), so it seems inappropriate for back branches, but presumably all future changes will be tested in HEAD anyway. In HEAD and 9.3, also clean up a whole lot of sloppiness in the emitted SQL for \dy (event triggers): lack of translatability due to failing to pass words-to-be-translated through gettext_noop(), inadequate schema qualification, and sloppy formatting resulting in unnecessarily ugly -E output. Peter Eisentraut and Tom Lane, per bug #8702 from Sergey Burladyan --- src/bin/psql/describe.c | 44 ++++++++++++++++++++++++------------ src/bin/psql/print.c | 4 ++++ src/bin/psql/print.h | 1 + src/bin/scripts/createlang.c | 2 ++ src/bin/scripts/droplang.c | 2 ++ 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 96322ca85b..d6d9c605cc 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -224,7 +224,7 @@ describeFunctions(const char *functypes, const char *pattern, bool verbose, bool PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, true, true, false, false, false, false}; + static const bool translate_columns[] = {false, false, false, false, true, true, true, false, false, false, false}; if (strlen(functypes) != strspn(functypes, "antwS+")) { @@ -457,6 +457,7 @@ describeFunctions(const char *functypes, const char *pattern, bool verbose, bool myopt.title = _("List of functions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -789,6 +790,7 @@ permissionsList(const char *pattern) myopt.title = buf.data; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -862,6 +864,7 @@ listDefaultACLs(const char *pattern) myopt.title = buf.data; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -1034,6 +1037,7 @@ objectDescription(const char *pattern, bool showSystem) myopt.title = _("Object descriptions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -2818,6 +2822,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys myopt.title = _("List of relations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); } @@ -2999,7 +3004,8 @@ listConversions(const char *pattern, bool verbose, bool showSystem) PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, true}; + static const bool translate_columns[] = + {false, false, false, false, true, false}; initPQExpBuffer(&buf); @@ -3055,6 +3061,7 @@ listConversions(const char *pattern, bool verbose, bool showSystem) myopt.title = _("List of conversions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -3079,19 +3086,23 @@ listEventTriggers(const char *pattern, bool verbose) initPQExpBuffer(&buf); printfPQExpBuffer(&buf, - "select evtname as \"%s\", " - "evtevent as \"%s\", " - "pg_catalog.pg_get_userbyid(e.evtowner) AS \"%s\", " - "case evtenabled when 'O' then 'enabled' " - " when 'R' then 'replica' " - " when 'A' then 'always' " - " when 'D' then 'disabled' end as \"%s\", " - "e.evtfoid::regproc as \"%s\", " - "array_to_string(array(select x " - " from unnest(evttags) as t(x)), ', ') as \"%s\" ", + "SELECT evtname as \"%s\", " + "evtevent as \"%s\", " + "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n" + " case evtenabled when 'O' then '%s'" + " when 'R' then '%s'" + " when 'A' then '%s'" + " when 'D' then '%s' end as \"%s\",\n" + " e.evtfoid::pg_catalog.regproc as \"%s\", " + "pg_catalog.array_to_string(array(select x" + " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"", gettext_noop("Name"), gettext_noop("Event"), gettext_noop("Owner"), + gettext_noop("enabled"), + gettext_noop("replica"), + gettext_noop("always"), + gettext_noop("disabled"), gettext_noop("Enabled"), gettext_noop("Procedure"), gettext_noop("Tags")); @@ -3100,7 +3111,7 @@ listEventTriggers(const char *pattern, bool verbose) ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"", gettext_noop("Description")); appendPQExpBufferStr(&buf, - "\nFROM pg_event_trigger e "); + "\nFROM pg_catalog.pg_event_trigger e "); processSQLNamePattern(pset.db, &buf, pattern, false, false, NULL, "evtname", NULL, NULL); @@ -3116,6 +3127,7 @@ listEventTriggers(const char *pattern, bool verbose) myopt.title = _("List of event triggers"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -3134,7 +3146,7 @@ listCasts(const char *pattern, bool verbose) PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, true}; + static const bool translate_columns[] = {false, false, false, true, false}; initPQExpBuffer(&buf); @@ -3214,6 +3226,7 @@ listCasts(const char *pattern, bool verbose) myopt.title = _("List of casts"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -3289,6 +3302,7 @@ listCollations(const char *pattern, bool verbose, bool showSystem) myopt.title = _("List of collations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -3548,6 +3562,7 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname) myopt.topt.default_footer = false; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); @@ -3579,6 +3594,7 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname) myopt.topt.default_footer = true; myopt.translate_header = true; myopt.translate_columns = NULL; + myopt.n_translate_columns = 0; printQuery(res, &myopt, pset.queryFout, pset.logfile); diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 736225c629..fdf4dcc22e 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -2596,6 +2596,10 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f printTableInit(&cont, &opt->topt, opt->title, PQnfields(result), PQntuples(result)); + /* Assert caller supplied enough translate_columns[] entries */ + Assert(opt->translate_columns == NULL || + opt->n_translate_columns >= cont.ncolumns); + for (i = 0; i < cont.ncolumns; i++) { char align; diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 9cfa3b6a41..41ba798985 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -146,6 +146,7 @@ typedef struct printQueryOpt bool translate_header; /* do gettext on column headers */ const bool *translate_columns; /* translate_columns[i-1] => do * gettext on col i */ + int n_translate_columns; /* length of translate_columns[] */ } printQueryOpt; diff --git a/src/bin/scripts/createlang.c b/src/bin/scripts/createlang.c index 5cfba8e3d5..92ab97540f 100644 --- a/src/bin/scripts/createlang.c +++ b/src/bin/scripts/createlang.c @@ -160,6 +160,8 @@ main(int argc, char *argv[]) popt.title = _("Procedural Languages"); popt.translate_header = true; popt.translate_columns = translate_columns; + popt.n_translate_columns = lengthof(translate_columns); + printQuery(result, &popt, stdout, NULL); PQfinish(conn); diff --git a/src/bin/scripts/droplang.c b/src/bin/scripts/droplang.c index b9664a9185..3650096f73 100644 --- a/src/bin/scripts/droplang.c +++ b/src/bin/scripts/droplang.c @@ -159,6 +159,8 @@ main(int argc, char *argv[]) popt.title = _("Procedural Languages"); popt.translate_header = true; popt.translate_columns = translate_columns; + popt.n_translate_columns = lengthof(translate_columns); + printQuery(result, &popt, stdout, NULL); PQfinish(conn); -- 2.40.0