Better title output for psql \dt \di etc. commands
Please find attached a patch to enable more intuitive titles when using
psql and doing the following actions:
\dt \di \dv \dm \ds \dE
In other words, everything in listTables()
We still default to "List or relations", but if we know that the user has
requested exactly one specific type of relation, we use that instead. So
instead of
greg=# \dv
List of relations
Schema | Name | Type | Owner
--------+-------------------------+------+-------
public | pg_stat_statements | view | greg
public | pg_stat_statements_info | view | greg
we get:
greg=# \dv
List of views
Schema | Name | Type | Owner
--------+-------------------------+------+-------
public | pg_stat_statements | view | greg
public | pg_stat_statements_info | view | greg
(2 rows)
The same applies for "misses" as well:
greg=# \di
Did not find any indexes.
greg=# \di foo*
Did not find any indexes named "foo*".
I thought about keeping that last one as singular, as it is replacing "Did
not find any relation named", but not only does it make more sense as a
plural given that wildcards can produce multiple relations, it makes the
code cleaner. :)
Inspired by a recent thread[0]/messages/by-id/CADrHaBEVSYwemoJWtry2+82KHy9tZirH2PVfi-uD96R5J8FCsw@mail.gmail.com over in pgsql-bugs entitled "Bug in psql".
(while not a bug, it is certainly a welcome enhancement, IMHO).
Cheers,
Greg
[0]: /messages/by-id/CADrHaBEVSYwemoJWtry2+82KHy9tZirH2PVfi-uD96R5J8FCsw@mail.gmail.com
/messages/by-id/CADrHaBEVSYwemoJWtry2+82KHy9tZirH2PVfi-uD96R5J8FCsw@mail.gmail.com
Attachments:
0001-Show-more-intuitive-titles-for-psql-commands.patchapplication/x-patch; name=0001-Show-more-intuitive-titles-for-psql-commands.patchDownload
From eaf1097eb0ac02d2ab093e4e4261d0a15adf8ac2 Mon Sep 17 00:00:00 2001
From: Greg Sabino Mullane <greg@turnstep.com>
Date: Mon, 3 Feb 2025 12:19:14 -0500
Subject: [PATCH] Show more intuitive titles for psql commands \dt \di \dv \dm
\ds and \dE
This applies to the modified versions as well e.g. \dt+ \dtS+
Rather than say "List of relations", if someone has entered in a \di, we say "List of indexes"
Per complaint on the bugs list on Jan 29, 2025 from xpusostomos@gmail.com
---
src/bin/psql/describe.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index aa4363b200..19366be2c8 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4012,9 +4012,11 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showForeign = strchr(tabtypes, 'E') != NULL;
PQExpBufferData buf;
+ PQExpBufferData title;
PGresult *res;
printQueryOpt myopt = pset.popt;
int cols_so_far;
+ char *item_type;
bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
/* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
@@ -4161,6 +4163,15 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
if (!res)
return false;
+ item_type = psprintf("%s",
+ (strspn(tabtypes, "tS+") == strlen(tabtypes)) ? "tables" :
+ (strspn(tabtypes, "iS+") == strlen(tabtypes)) ? "indexes" :
+ (strspn(tabtypes, "vS+") == strlen(tabtypes)) ? "views" :
+ (strspn(tabtypes, "mS+") == strlen(tabtypes)) ? "materialized view" :
+ (strspn(tabtypes, "sS+") == strlen(tabtypes)) ? "sequences" :
+ (strspn(tabtypes, "ES+") == strlen(tabtypes)) ? "foreign tables" :
+ "relations");
+
/*
* Most functions in this file are content to print an empty table when
* there are no matching objects. We intentionally deviate from that
@@ -4169,14 +4180,16 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
- pg_log_error("Did not find any relation named \"%s\".",
- pattern);
+ pg_log_error("Did not find any %s named \"%s\".", item_type, pattern);
else
- pg_log_error("Did not find any relations.");
+ pg_log_error("Did not find any %s.", item_type);
}
else
{
- myopt.title = _("List of relations");
+ initPQExpBuffer(&title);
+ printfPQExpBuffer(&title, _("List of %s"), item_type);
+
+ myopt.title = title.data;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
myopt.n_translate_columns = lengthof(translate_columns);
--
2.30.2
Greg Sabino Mullane <htamfids@gmail.com> writes:
Please find attached a patch to enable more intuitive titles when using
psql and doing the following actions:
\dt \di \dv \dm \ds \dE
In other words, everything in listTables()
We still default to "List or relations", but if we know that the user has
requested exactly one specific type of relation, we use that
instead.
As presented, I think this fails the translatability guideline about
"Do not construct sentences at run-time" [1]https://www.postgresql.org/docs/devel/nls-programmer.html#NLS-GUIDELINES. You could get around
that by selecting one of several whole title strings, though.
For myself, if we were going to do something like this, I'd kind
of like to cover more cases:
greg=# \dti
List of tables and indexes
But I'm really not sure how to maintain translatability without
a combinatorial explosion. It's fairly easy to see how we might
produce output like
greg=# \dtvi
List of tables, views, indexes
but "List of", "tables", "views", and "indexes" would all have
to be separately translated, and I fear that might not hang
together well.
regards, tom lane
[1]: https://www.postgresql.org/docs/devel/nls-programmer.html#NLS-GUIDELINES
On Mon, Feb 3, 2025 at 1:07 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
As presented, I think this fails the translatability guideline
Thanks, good point. Please find attached a slightly more verbose version
that should be better for translations.
For myself, if we were going to do something like this, I'd kind of like to
cover more cases:
greg=# \dti
List of tables and indexes
I toyed with that for a bit, but as you say, without generating a ton of
combinations to translate, we'd have to fall back on run-time
constructions. Neither is ideal. I also realized that I almost never type
"\dti". Very common for me are \d and \dt and \dv etc. but combinations are
something I never bother with. At that point, I just do a \d. I think given
how rare (granted, anecdotally) those combinations are, it's okay if we
expose people to the "r" word.
Cheers,
Greg
--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support
Attachments:
0002-Show-more-intuitive-titles-for-psql-commands.patchapplication/octet-stream; name=0002-Show-more-intuitive-titles-for-psql-commands.patchDownload
From de656c1f589264ed9d376fd5726714bb28a0224a Mon Sep 17 00:00:00 2001
From: Greg Sabino Mullane <greg@turnstep.com>
Date: Mon, 3 Feb 2025 14:27:49 -0500
Subject: [PATCH] Show more intuitive titles for psql commands \dt \di \dv \dm
\ds and \dE
This applies to the modified versions as well e.g. \dt+ \dtS+
Rather than say "List of relations", if someone has entered in a \di, we say "List of indexes"
Per complaint on the bugs list on Jan 29, 2025 from xpusostomos@gmail.com
---
src/bin/psql/describe.c | 48 +++++++++++++++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 4 deletions(-)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index aa4363b200..24faf44e40 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4012,6 +4012,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showForeign = strchr(tabtypes, 'E') != NULL;
PQExpBufferData buf;
+ PQExpBufferData title;
PGresult *res;
printQueryOpt myopt = pset.popt;
int cols_so_far;
@@ -4169,14 +4170,53 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
- pg_log_error("Did not find any relation named \"%s\".",
- pattern);
+ {
+ if (strspn(tabtypes, "tS+") == strlen(tabtypes))
+ pg_log_error("Did not find any tables named \"%s\".", pattern);
+ else if (strspn(tabtypes, "iS+") == strlen(tabtypes))
+ pg_log_error("Did not find any indexes named \"%s\".", pattern);
+ else if (strspn(tabtypes, "vS+") == strlen(tabtypes))
+ pg_log_error("Did not find any views named \"%s\".", pattern);
+ else if (strspn(tabtypes, "mS+") == strlen(tabtypes))
+ pg_log_error("Did not find any materialized views named \"%s\".", pattern);
+ else if (strspn(tabtypes, "sS+") == strlen(tabtypes))
+ pg_log_error("Did not find any sequences named \"%s\".", pattern);
+ else if (strspn(tabtypes, "ES+") == strlen(tabtypes))
+ pg_log_error("Did not find any foreign tables named \"%s\".", pattern);
+ else
+ pg_log_error("Did not find any relations named \"%s\".", pattern);
+ }
else
- pg_log_error("Did not find any relations.");
+ {
+ if (strspn(tabtypes, "tS+") == strlen(tabtypes))
+ pg_log_error("Did not find any tables.");
+ else if (strspn(tabtypes, "iS+") == strlen(tabtypes))
+ pg_log_error("Did not find any indexes.");
+ else if (strspn(tabtypes, "vS+") == strlen(tabtypes))
+ pg_log_error("Did not find any views.");
+ else if (strspn(tabtypes, "mS+") == strlen(tabtypes))
+ pg_log_error("Did not find any materialized views.");
+ else if (strspn(tabtypes, "sS+") == strlen(tabtypes))
+ pg_log_error("Did not find any sequences.");
+ else if (strspn(tabtypes, "ES+") == strlen(tabtypes))
+ pg_log_error("Did not find any foreign tables.");
+ else
+ pg_log_error("Did not find any relations.");
+ }
}
else
{
- myopt.title = _("List of relations");
+ initPQExpBuffer(&title);
+ printfPQExpBuffer(&title,
+ (strspn(tabtypes, "tS+") == strlen(tabtypes)) ? _("List of tables") :
+ (strspn(tabtypes, "iS+") == strlen(tabtypes)) ? _("List of indexes") :
+ (strspn(tabtypes, "vS+") == strlen(tabtypes)) ? _("List of views") :
+ (strspn(tabtypes, "mS+") == strlen(tabtypes)) ? _("List of materialized view") :
+ (strspn(tabtypes, "sS+") == strlen(tabtypes)) ? _("List of sequences") :
+ (strspn(tabtypes, "ES+") == strlen(tabtypes)) ? _("List of foreign tables") :
+ "List of relations");
+
+ myopt.title = title.data;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
myopt.n_translate_columns = lengthof(translate_columns);
--
2.30.2
Greg Sabino Mullane <htamfids@gmail.com> writes:
I toyed with that for a bit, but as you say, without generating a ton of
combinations to translate, we'd have to fall back on run-time
constructions. Neither is ideal. I also realized that I almost never type
"\dti". Very common for me are \d and \dt and \dv etc. but combinations are
something I never bother with. At that point, I just do a \d. I think given
how rare (granted, anecdotally) those combinations are, it's okay if we
expose people to the "r" word.
Fair. This is already a step forward, so it doesn't have to be
perfect.
Looking at the code, I'm not thrilled with the strspn() coding
method. I'd much rather it relied on the bool flags we already
computed (showTables etc). I'm wondering about adding a step like
int ntypes = (int) showTables + (int) showIndexes + ...
(the explicit coercions to int probably aren't necessary)
and then the code could look like
(ntypes != 1) ? _("List of relations") :
(showTables) ? _("List of tables") :
(showIndexes) ? _("List of indexes") :
...
"ntypes" could also be used to simplify the logic that forces
all the flags on, up at the top of the function.
regards, tom lane
On Mon, Feb 03, 2025 at 03:12:57PM -0500, Tom Lane wrote:
Greg Sabino Mullane <htamfids@gmail.com> writes:
I toyed with that for a bit, but as you say, without generating a ton of
combinations to translate, we'd have to fall back on run-time
constructions. Neither is ideal. I also realized that I almost never type
"\dti". Very common for me are \d and \dt and \dv etc. but combinations are
something I never bother with. At that point, I just do a \d. I think given
how rare (granted, anecdotally) those combinations are, it's okay if we
expose people to the "r" word.Fair. This is already a step forward, so it doesn't have to be
perfect.
+1. I don't use combinations like \dti regularly, either.
--
nathan
I like the ntypes idea: please find attached version 3 which implements it.
I wrote it slightly differently as I did not want to leave any chance of it
escaping the if else blocks without a match.
Cheers,
Greg
--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support
Attachments:
0003-Show-more-intuitive-titles-for-psql-commands.patchapplication/octet-stream; name=0003-Show-more-intuitive-titles-for-psql-commands.patchDownload
From 38847cfdbf712bc9ccbbca68a26b3f7672a783e5 Mon Sep 17 00:00:00 2001
From: Greg Sabino Mullane <greg@turnstep.com>
Date: Mon, 3 Feb 2025 16:20:18 -0500
Subject: [PATCH] Show more intuitive titles for psql commands \dt \di \dv \dm
\ds and \dE
This applies to the modified versions as well e.g. \dt+ \dtS+
Rather than say "List of relations", if someone has entered in a \di, we say "List of indexes"
Per complaint on the bugs list on Jan 29, 2025 from xpusostomos@gmail.com
---
src/bin/psql/describe.c | 53 +++++++++++++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index aa4363b200..c442d1a565 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4011,14 +4011,17 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
+ int ntypes = showTables + showIndexes + showViews + showMatViews + showSeq + showForeign;
+
PQExpBufferData buf;
+ PQExpBufferData title;
PGresult *res;
printQueryOpt myopt = pset.popt;
int cols_so_far;
bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
/* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
- if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
+ if (ntypes < 1)
showTables = showViews = showMatViews = showSeq = showForeign = true;
initPQExpBuffer(&buf);
@@ -4169,14 +4172,54 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
- pg_log_error("Did not find any relation named \"%s\".",
- pattern);
+ {
+ if (ntypes == 1 && showTables)
+ pg_log_error("Did not find any tables named \"%s\".", pattern);
+ else if (ntypes == 1 && showIndexes)
+ pg_log_error("Did not find any indexes named \"%s\".", pattern);
+ else if (ntypes == 1 && showViews)
+ pg_log_error("Did not find any views named \"%s\".", pattern);
+ else if (ntypes == 1 && showMatViews)
+ pg_log_error("Did not find any materialized views named \"%s\".", pattern);
+ else if (ntypes == 1 && showSeq)
+ pg_log_error("Did not find any sequences named \"%s\".", pattern);
+ else if (ntypes == 1 && showForeign)
+ pg_log_error("Did not find any foreign tables named \"%s\".", pattern);
+ else
+ pg_log_error("Did not find any relations named \"%s\".", pattern);
+
+ }
else
- pg_log_error("Did not find any relations.");
+ {
+ if (ntypes == 1 && showTables)
+ pg_log_error("Did not find any tables.");
+ else if (ntypes == 1 && showIndexes)
+ pg_log_error("Did not find any indexes.");
+ else if (ntypes == 1 && showViews)
+ pg_log_error("Did not find any views.");
+ else if (ntypes == 1 && showMatViews)
+ pg_log_error("Did not find any materialized views.");
+ else if (ntypes == 1 && showSeq)
+ pg_log_error("Did not find any sequences.");
+ else if (ntypes == 1 && showForeign)
+ pg_log_error("Did not find any foreign tables.");
+ else
+ pg_log_error("Did not find any relations.");
+ }
}
else
{
- myopt.title = _("List of relations");
+ initPQExpBuffer(&title);
+ printfPQExpBuffer(&title,
+ (ntypes == 1 && showTables) ? _("List of tables") :
+ (ntypes == 1 && showIndexes) ? _("List of indexes") :
+ (ntypes == 1 && showViews) ? _("List of views") :
+ (ntypes == 1 && showMatViews) ? _("List of materialized views") :
+ (ntypes == 1 && showSeq) ? _("List of sequences") :
+ (ntypes == 1 && showForeign) ? _("List of foreign tables") :
+ _("List of relations"));
+
+ myopt.title = title.data;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
myopt.n_translate_columns = lengthof(translate_columns);
--
2.30.2
Greg Sabino Mullane <htamfids@gmail.com> writes:
I like the ntypes idea: please find attached version 3 which implements it.
I wrote it slightly differently as I did not want to leave any chance of it
escaping the if else blocks without a match.
Hmm, I was envisioning being a little more in-your-face about the
no-match case, because that would surely represent somebody failing
to update the code correctly when adding a new relation kind.
So I was imagining something like the attached.
One problem with it is that while we can leave "List of ???" out
of the set of translatable strings easily, we can't currently
do that for the argument of pg_log_error because it's automatically
a gettext trigger. I'd rather not burden translators with figuring
out what to do with that. Is it worth creating pg_log_error_internal,
equivalently to elog and errmsg_internal in the backend?
BTW, I updated the regression tests for this, and that bears out
your argument that one-type commands are the majority. There
are still a couple "List of relations", but not many.
regards, tom lane
Attachments:
0004-Show-more-intuitive-titles-for-psql-commands.patchtext/x-diff; charset=us-ascii; name=0004-Show-more-intuitive-titles-for-psql-commands.patchDownload
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index aa4363b200..b314fb5d71 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4011,14 +4011,18 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
+ int ntypes;
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
int cols_so_far;
bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
- /* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
- if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
+ /* Count the number of explicitly-requested relation types */
+ ntypes = showTables + showIndexes + showViews + showMatViews +
+ showSeq + showForeign;
+ /* If none, we default to \dtvmsE (but see also command.c) */
+ if (ntypes == 0)
showTables = showViews = showMatViews = showSeq = showForeign = true;
initPQExpBuffer(&buf);
@@ -4169,14 +4173,55 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
- pg_log_error("Did not find any relation named \"%s\".",
- pattern);
+ {
+ if (ntypes != 1)
+ pg_log_error("Did not find any relations named \"%s\".", pattern);
+ else if (showTables)
+ pg_log_error("Did not find any tables named \"%s\".", pattern);
+ else if (showIndexes)
+ pg_log_error("Did not find any indexes named \"%s\".", pattern);
+ else if (showViews)
+ pg_log_error("Did not find any views named \"%s\".", pattern);
+ else if (showMatViews)
+ pg_log_error("Did not find any materialized views named \"%s\".", pattern);
+ else if (showSeq)
+ pg_log_error("Did not find any sequences named \"%s\".", pattern);
+ else if (showForeign)
+ pg_log_error("Did not find any foreign tables named \"%s\".", pattern);
+ else /* should not get here */
+ pg_log_error("Did not find any ??? named \"%s\".", pattern);
+ }
else
- pg_log_error("Did not find any relations.");
+ {
+ if (ntypes != 1)
+ pg_log_error("Did not find any relations.");
+ else if (showTables)
+ pg_log_error("Did not find any tables.");
+ else if (showIndexes)
+ pg_log_error("Did not find any indexes.");
+ else if (showViews)
+ pg_log_error("Did not find any views.");
+ else if (showMatViews)
+ pg_log_error("Did not find any materialized views.");
+ else if (showSeq)
+ pg_log_error("Did not find any sequences.");
+ else if (showForeign)
+ pg_log_error("Did not find any foreign tables.");
+ else /* should not get here */
+ pg_log_error("Did not find any ??? relations.");
+ }
}
else
{
- myopt.title = _("List of relations");
+ myopt.title =
+ (ntypes != 1) ? _("List of relations") :
+ (showTables) ? _("List of tables") :
+ (showIndexes) ? _("List of indexes") :
+ (showViews) ? _("List of views") :
+ (showMatViews) ? _("List of materialized views") :
+ (showSeq) ? _("List of sequences") :
+ (showForeign) ? _("List of foreign tables") :
+ "List of ???"; /* should not get here */
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
myopt.n_translate_columns = lengthof(translate_columns);
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index 74d9ff2998..75a078ada9 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -116,7 +116,7 @@ FROM pg_type JOIN pg_class c ON typrelid = c.oid WHERE typname = 'deptest_t';
RESET SESSION AUTHORIZATION;
REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user2;
\dt deptest
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+---------+-------+-------------------
public | deptest | table | regress_dep_user2
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index e6f7b9013d..f9db4032e1 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -3027,7 +3027,7 @@ Access method: heap
(4 rows)
\dt+
- List of relations
+ List of tables
Schema | Name | Type | Owner | Persistence | Access method | Size | Description
-----------------+---------------+-------+----------------------+-------------+---------------+---------+-------------
tableam_display | tbl_heap | table | regress_display_role | permanent | heap | 0 bytes |
@@ -3035,7 +3035,7 @@ Access method: heap
(2 rows)
\dm+
- List of relations
+ List of materialized views
Schema | Name | Type | Owner | Persistence | Access method | Size | Description
-----------------+--------------------+-------------------+----------------------+-------------+---------------+---------+-------------
tableam_display | mat_view_heap_psql | materialized view | regress_display_role | permanent | heap_psql | 0 bytes |
@@ -3043,7 +3043,7 @@ Access method: heap
-- But not for views and sequences.
\dv+
- List of relations
+ List of views
Schema | Name | Type | Owner | Persistence | Size | Description
-----------------+----------------+------+----------------------+-------------+---------+-------------
tableam_display | view_heap_psql | view | regress_display_role | permanent | 0 bytes |
@@ -6244,7 +6244,7 @@ List of access methods
(0 rows)
\dt "no.such.table.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
@@ -6316,31 +6316,31 @@ List of access methods
(0 rows)
\di "no.such.index.relation"
- List of relations
+ List of indexes
Schema | Name | Type | Owner | Table
--------+------+------+-------+-------
(0 rows)
\dm "no.such.materialized.view"
- List of relations
+ List of materialized views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\ds "no.such.relation"
- List of relations
+ List of sequences
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dt "no.such.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dv "no.such.relation"
- List of relations
+ List of views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
@@ -6474,7 +6474,7 @@ List of schemas
\dA "no.such.schema"."no.such.access.method"
improper qualified name (too many dotted names): "no.such.schema"."no.such.access.method"
\dt "no.such.schema"."no.such.table.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
@@ -6526,31 +6526,31 @@ improper qualified name (too many dotted names): "no.such.schema"."no.such.table
(0 rows)
\di "no.such.schema"."no.such.index.relation"
- List of relations
+ List of indexes
Schema | Name | Type | Owner | Table
--------+------+------+-------+-------
(0 rows)
\dm "no.such.schema"."no.such.materialized.view"
- List of relations
+ List of materialized views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\ds "no.such.schema"."no.such.relation"
- List of relations
+ List of sequences
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dt "no.such.schema"."no.such.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dv "no.such.schema"."no.such.relation"
- List of relations
+ List of views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
@@ -6641,7 +6641,7 @@ improper qualified name (too many dotted names): "no.such.schema"."no.such.insta
improper qualified name (too many dotted names): "no.such.schema"."no.such.event.trigger"
-- again, but with current database and dotted schema qualifications.
\dt regression."no.such.schema"."no.such.table.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
@@ -6677,31 +6677,31 @@ improper qualified name (too many dotted names): "no.such.schema"."no.such.event
(0 rows)
\di regression."no.such.schema"."no.such.index.relation"
- List of relations
+ List of indexes
Schema | Name | Type | Owner | Table
--------+------+------+-------+-------
(0 rows)
\dm regression."no.such.schema"."no.such.materialized.view"
- List of relations
+ List of materialized views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\ds regression."no.such.schema"."no.such.relation"
- List of relations
+ List of sequences
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dt regression."no.such.schema"."no.such.relation"
- List of relations
+ List of tables
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
\dv regression."no.such.schema"."no.such.relation"
- List of relations
+ List of views
Schema | Name | Type | Owner
--------+------+------+-------
(0 rows)
On Mon, Feb 3, 2025 at 5:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
One problem with it is that while we can leave "List of ???" out of the
set of translatable strings easily, we can't currently do that for the
argument of pg_log_error because it's automatically a gettext trigger. I'd
rather not burden translators with figuring out what to do with that. Is
it worth creating pg_log_error_internal, equivalently to elog and
errmsg_internal in the backend?
I don't think so, unless there are other uses of it waiting. For this
particular item, I'm still of the opinion that leaving it as "List of
relations" is a pretty good default for some future somebody who forgets to
update the lists of relation types. New types are certainly not going to
happen often. Better "List of relations" than a release where everybody and
their cousin starts asking what "List of ???" means.
BTW, I updated the regression tests for this, and that bears out your
argument that one-type commands are the majority. There are still a couple
"List of relations", but not many.
Thank you for that, I should have done that in my original patch.
Cheers,
Greg
P.S. I found it amusing to see in some quick grepping that pg_controldata
has a msgstr of "???". So far no language has managed to translate it into
something else.
--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support
Greg Sabino Mullane <htamfids@gmail.com> writes:
... For this
particular item, I'm still of the opinion that leaving it as "List of
relations" is a pretty good default for some future somebody who forgets to
update the lists of relation types. New types are certainly not going to
happen often. Better "List of relations" than a release where everybody and
their cousin starts asking what "List of ???" means.
Hopefully an oversight like that wouldn't make it to release. But
hiding it by putting out an ordinary-looking title might help it
avoid detection for a long time.
P.S. I found it amusing to see in some quick grepping that pg_controldata
has a msgstr of "???". So far no language has managed to translate it into
something else.
There are several pre-existing "???" strings in describe.c too,
for similar this-shouldn't-happen cases. I'm not proposing
something that's far outside our existing practice.
regards, tom lane
On 2025-Feb-03, Tom Lane wrote:
One problem with it is that while we can leave "List of ???" out
of the set of translatable strings easily, we can't currently
do that for the argument of pg_log_error because it's automatically
a gettext trigger. I'd rather not burden translators with figuring
out what to do with that. Is it worth creating pg_log_error_internal,
equivalently to elog and errmsg_internal in the backend?
At this point I would just add a "translator:" comment that explains
that the ??? bit is for unexpected cases and can be translated in the
same way.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"The problem with the future is that it keeps turning into the present"
(Hobbes)
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@alvh.no-ip.org> writes:
At this point I would just add a "translator:" comment that explains
that the ??? bit is for unexpected cases and can be translated in the
same way.
Hmm, do we have a standard policy or comment wording about that?
I looked for "translator: ... unexpected" and didn't find any
existing comments of that sort.
regards, tom lane
On 2025-Feb-04, Tom Lane wrote:
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@alvh.no-ip.org> writes:
At this point I would just add a "translator:" comment that explains
that the ??? bit is for unexpected cases and can be translated in the
same way.Hmm, do we have a standard policy or comment wording about that?
I looked for "translator: ... unexpected" and didn't find any
existing comments of that sort.
I don't remember cases of messages of that kind marked for translation,
so I'm not surprised that we don't have any such comments. Those would
typically be elog() or the errfoo_internal() cases, I think.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"El sudor es la mejor cura para un pensamiento enfermo" (Bardia)
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@alvh.no-ip.org> writes:
On 2025-Feb-04, Tom Lane wrote:
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@alvh.no-ip.org> writes:
At this point I would just add a "translator:" comment that explains
that the ??? bit is for unexpected cases and can be translated in the
same way.
Hmm, do we have a standard policy or comment wording about that?
I looked for "translator: ... unexpected" and didn't find any
existing comments of that sort.
I don't remember cases of messages of that kind marked for translation,
so I'm not surprised that we don't have any such comments. Those would
typically be elog() or the errfoo_internal() cases, I think.
Yeah, that's what I would have thought.
The implementation I had in mind was to just invent a
pg_log_error_internal() macro alias for pg_log_error().
That'd take about two lines counting the explanatory comment.
This approach would fail to suppress the cost of gettext's
trying to look up the string, but surely we aren't concerned
about that here --- we just want to not burden translators
with the string. (I need to check that gettext isn't smart
enough to see through a macro, though. If it is, a static
inline function should do.)
regards, tom lane
On 2025-Feb-04, Tom Lane wrote:
The implementation I had in mind was to just invent a
pg_log_error_internal() macro alias for pg_log_error().
That'd take about two lines counting the explanatory comment.
This approach would fail to suppress the cost of gettext's
trying to look up the string, but surely we aren't concerned
about that here --- we just want to not burden translators
with the string.
Yeah, okay, that seems good enough for me. I agree that the cost of an
unnecessary gettext() call is negligible.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@alvh.no-ip.org> writes:
On 2025-Feb-04, Tom Lane wrote:
The implementation I had in mind was to just invent a
pg_log_error_internal() macro alias for pg_log_error().
That'd take about two lines counting the explanatory comment.
This approach would fail to suppress the cost of gettext's
trying to look up the string, but surely we aren't concerned
about that here --- we just want to not burden translators
with the string.
Yeah, okay, that seems good enough for me. I agree that the cost of an
unnecessary gettext() call is negligible.
Sounds good, I'll proceed along those lines.
regards, tom lane