On 2023-10-07 14:29 +0200, Laurenz Albe write: > On Sat, 2023-10-07 at 05:07 +0200, Erik Wienhold wrote: > > On 2023-10-06 22:32 +0200, Laurenz Albe write: > > > On Sun, 2023-09-17 at 21:31 +0200, Erik Wienhold wrote: > > > > I wrote a patch to change psql's display of zero privileges after a > > > > user's > > > > reported confusion with the psql output for zero vs. default privileges > > > > [1]. > > > > Admittedly, zero privileges is a rare use case [2] but I think psql > > > > should not > > > > confuse the user in the off chance that this happens. > > > > > > > > [1] > > > > https://www.postgresql.org/message-id/efdd465d-a795-6188-7f71-7cdb4b2be031%40mtneva.com > > > > [2] > > > > https://www.postgresql.org/message-id/31246.1693337238%40sss.pgh.pa.us > > > > > > Reading that thread, I had the impression that there was more support for > > > honoring "\pset null" rather than unconditionally displaying "(none)". > > > > For example with your patch applied: > > > > create table t1 (a int); > > create table t2 (a int); > > create table t3 (a int); > > > > revoke all on t2 from :USER; > > > > \pset null <NULL> > > \dp t1|t2|t3 > > Access privileges > > Schema | Name | Type | Access privileges | Column privileges | > > Policies > > > > --------+------+-------+-------------------+-------------------+---------- > > public | t1 | table | <NULL> | | > > public | t2 | table | | | > > public | t3 | table | <NULL> | | > > (3 rows) > > > > Instead of only displaying the zero privileges with my patch and default > > \pset null: > > > > \pset null '' > > \dp t1|t2|t3 > > Access privileges > > Schema | Name | Type | Access privileges | Column privileges | > > Policies > > > > --------+------+-------+-------------------+-------------------+---------- > > public | t1 | table | | | > > public | t2 | table | (none) | | > > public | t3 | table | | | > > (3 rows) > > > > I guess if most tables have any non-default privileges then both > > solutions are equally good. > > It is a tough call. > > For somebody who knows PostgreSQL well enough to know that default > privileges are represented by NULL values, my solution is probably > more appealing. > > It seems that we both had the goal of distinguishing the cases of > default and zero privileges, but for a beginner, both versions are > confusing. better would probably be > > Access privileges > Schema | Name | Type | Access privileges | Column privileges | Policies > --------+------+-------+-------------------+-------------------+---------- > public | t1 | table | default | default | > public | t2 | table | | default | > public | t3 | table | default | default |
Ah yes. The problem seems to be more with default privileges producing no output right now. I was just focusing on the zero privs edge case. > The disadvantage of this (and the advantage of my proposal) is that it > might confuse experienced users (and perhaps automated tools) if the > output changes too much. I agree that your patch is less invasive under default settings. But is the output of meta commands considered part of the interface where we need to be cautious about not breaking clients? I've written quite a few scripts that parse results from psql's stdout, but always with simple queries to have control over columns and the formatting of values. I always expect meta command output to change with the next release because to me they look more like a human-readable interface, e.g. the localizable header which of course one can still hide with --tuples-only. > > > The simple attached patch does it like that. What do you think? > > > > LGTM. > > If you are happy enough with my patch, shall we mark it as ready for > committer? I amended your patch to also document the effect of \pset null in this case. See the attached v2. > Or do you want to have a stab at something like I suggested above? Not right now if the user can just use \pset null 'default' with your patch. -- Erik
>From a3ea9549a4467fb4ab186d652e0afec6adc2f725 Mon Sep 17 00:00:00 2001 From: Laurenz Albe <laurenz.a...@cybertec.at> Date: Fri, 6 Oct 2023 22:12:33 +0200 Subject: [PATCH v2] psql: honor "\pset null" in backslash commands In the output of backslash commands, NULL was always displayed as empty string, rather than honoring the values set with "\pset null". This was surprising, and in some cases it was downright confusing: for example, default privileges (stored as NULL) were displayed just like an empty aclitem[], making these cases undistinguishable in the output. Discussion: https://postgr.es/m/96d6885a-5e25-9ae8-4a1a-d7e557a5fe9c%40mtneva.com --- doc/src/sgml/ddl.sgml | 6 ++++-- src/bin/psql/describe.c | 43 ----------------------------------------- 2 files changed, 4 insertions(+), 45 deletions(-) diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 075ff32991..62a65429d6 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -2353,8 +2353,8 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw; <para> If the <quote>Access privileges</quote> column is empty for a given object, it means the object has default privileges (that is, its - privileges entry in the relevant system catalog is null). Default - privileges always include all privileges for the owner, and can include + privileges entry in the relevant system catalog is null) or zero privileges. + Default privileges always include all privileges for the owner, and can include some privileges for <literal>PUBLIC</literal> depending on the object type, as explained above. The first <command>GRANT</command> or <command>REVOKE</command> on an object will instantiate the default @@ -2362,6 +2362,8 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw; example, <literal>miriam=arwdDxt/miriam</literal>) and then modify them per the specified request. Similarly, entries are shown in <quote>Column privileges</quote> only for columns with nondefault privileges. + One might prefer <literal>\pset null '(null)'</literal> to distinguish + default from zero privileges. (Note: for this purpose, <quote>default privileges</quote> always means the built-in default privileges for the object's type. An object whose privileges have been affected by an <command>ALTER DEFAULT diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index bac94a338c..224aa22575 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -124,7 +124,6 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of aggregate functions"); myopt.translate_header = true; @@ -197,7 +196,6 @@ describeAccessMethods(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of access methods"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -262,7 +260,6 @@ describeTablespaces(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of tablespaces"); myopt.translate_header = true; @@ -585,7 +582,6 @@ describeFunctions(const char *functypes, const char *func_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of functions"); myopt.translate_header = true; if (pset.sversion >= 90600) @@ -702,7 +698,6 @@ describeTypes(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of data types"); myopt.translate_header = true; @@ -893,7 +888,6 @@ describeOperators(const char *oper_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of operators"); myopt.translate_header = true; @@ -995,7 +989,6 @@ listAllDbs(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of databases"); myopt.translate_header = true; @@ -1146,7 +1139,6 @@ permissionsList(const char *pattern, bool showSystem) if (!res) goto error_return; - myopt.nullPrint = NULL; printfPQExpBuffer(&buf, _("Access privileges")); myopt.title = buf.data; myopt.translate_header = true; @@ -1218,7 +1210,6 @@ listDefaultACLs(const char *pattern) if (!res) goto error_return; - myopt.nullPrint = NULL; printfPQExpBuffer(&buf, _("Default access privileges")); myopt.title = buf.data; myopt.translate_header = true; @@ -1417,7 +1408,6 @@ objectDescription(const char *pattern, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("Object descriptions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -3852,7 +3842,6 @@ listDbRoleSettings(const char *pattern, const char *pattern2) } else { - myopt.nullPrint = NULL; myopt.title = _("List of settings"); myopt.translate_header = true; @@ -3926,7 +3915,6 @@ describeRoleGrants(const char *pattern, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of role grants"); myopt.translate_header = true; @@ -4122,7 +4110,6 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys } else { - myopt.nullPrint = NULL; myopt.title = _("List of relations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -4332,7 +4319,6 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose) initPQExpBuffer(&title); appendPQExpBufferStr(&title, tabletitle); - myopt.nullPrint = NULL; myopt.title = title.data; myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -4412,7 +4398,6 @@ listLanguages(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of languages"); myopt.translate_header = true; @@ -4497,7 +4482,6 @@ listDomains(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of domains"); myopt.translate_header = true; @@ -4576,7 +4560,6 @@ listConversions(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of conversions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -4644,7 +4627,6 @@ describeConfigurationParameters(const char *pattern, bool verbose, if (!res) return false; - myopt.nullPrint = NULL; if (pattern) myopt.title = _("List of configuration parameters"); else @@ -4726,7 +4708,6 @@ listEventTriggers(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of event triggers"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -4825,7 +4806,6 @@ listExtendedStats(const char *pattern) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of extended statistics"); myopt.translate_header = true; @@ -4938,7 +4918,6 @@ listCasts(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of casts"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -5057,7 +5036,6 @@ listCollations(const char *pattern, bool verbose, bool showSystem) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of collations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -5119,7 +5097,6 @@ listSchemas(const char *pattern, bool verbose, bool showSystem) if (!res) goto error_return; - myopt.nullPrint = NULL; myopt.title = _("List of schemas"); myopt.translate_header = true; @@ -5236,7 +5213,6 @@ listTSParsers(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of text search parsers"); myopt.translate_header = true; @@ -5384,7 +5360,6 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname) if (!res) return false; - myopt.nullPrint = NULL; initPQExpBuffer(&title); if (nspname) printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""), @@ -5421,7 +5396,6 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname) return false; } - myopt.nullPrint = NULL; if (nspname) printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""), nspname, prsname); @@ -5497,7 +5471,6 @@ listTSDictionaries(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of text search dictionaries"); myopt.translate_header = true; @@ -5563,7 +5536,6 @@ listTSTemplates(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of text search templates"); myopt.translate_header = true; @@ -5618,7 +5590,6 @@ listTSConfigs(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of text search configurations"); myopt.translate_header = true; @@ -5764,7 +5735,6 @@ describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, appendPQExpBuffer(&title, _("\nParser: \"%s\""), prsname); - myopt.nullPrint = NULL; myopt.title = title.data; myopt.footers = NULL; myopt.topt.default_footer = false; @@ -5841,7 +5811,6 @@ listForeignDataWrappers(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of foreign-data wrappers"); myopt.translate_header = true; @@ -5918,7 +5887,6 @@ listForeignServers(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of foreign servers"); myopt.translate_header = true; @@ -5974,7 +5942,6 @@ listUserMappings(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of user mappings"); myopt.translate_header = true; @@ -6047,7 +6014,6 @@ listForeignTables(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of foreign tables"); myopt.translate_header = true; @@ -6099,7 +6065,6 @@ listExtensions(const char *pattern) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of installed extensions"); myopt.translate_header = true; @@ -6203,7 +6168,6 @@ listOneExtensionContents(const char *extname, const char *oid) if (!res) return false; - myopt.nullPrint = NULL; initPQExpBuffer(&title); printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname); myopt.title = title.data; @@ -6340,7 +6304,6 @@ listPublications(const char *pattern) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of publications"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -6695,7 +6658,6 @@ describeSubscriptions(const char *pattern, bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of subscriptions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -6808,7 +6770,6 @@ listOperatorClasses(const char *access_method_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of operator classes"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -6897,7 +6858,6 @@ listOperatorFamilies(const char *access_method_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of operator families"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -6996,7 +6956,6 @@ listOpFamilyOperators(const char *access_method_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of operators of operator families"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -7089,7 +7048,6 @@ listOpFamilyFunctions(const char *access_method_pattern, if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("List of support functions of operator families"); myopt.translate_header = true; myopt.translate_columns = translate_columns; @@ -7141,7 +7099,6 @@ listLargeObjects(bool verbose) if (!res) return false; - myopt.nullPrint = NULL; myopt.title = _("Large objects"); myopt.translate_header = true; -- 2.42.0