Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mNZHR-000679-OH for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Sep 2021 11:28:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mNZHQ-0007Yp-Cm for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Sep 2021 11:28:44 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mNZHQ-0007Yh-1c for pgsql-hackers@lists.postgresql.org; Tue, 07 Sep 2021 11:28:44 +0000 Received: from mail.postgrespro.ru ([93.174.131.139]) by magus.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mNZHL-00012r-F6 for pgsql-hackers@lists.postgresql.org; Tue, 07 Sep 2021 11:28:43 +0000 Received: from [10.0.1.8] (unknown [188.123.231.167]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mail.postgrespro.ru (Postfix) with ESMTPSA id D705921CD145; Tue, 7 Sep 2021 14:28:37 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mail; t=1631014117; bh=pkdbK22i0Jcx1E+pWccbLzYpCwvVa04ALghgTNBM2qs=; h=Subject:To:References:From:Date:In-Reply-To; b=qejXoCPbMCHl7POMUbIZat3RCt8CF5QpRxjTCjcIIvWb+U+NRrq8bsh6wItoecvzh 9ulKtmfNSW5zyB2dJ9JD9vuyE3EN2yu/8ohS1/Y1HVo9chUBMVdhJ+BCB1j2HxQlX6 CQUoD+rYQODvkHNdEAm12GTcmkUC+z4MxOW9Dkm8= Subject: Re: psql: \dl+ to list large objects privileges To: gkokolatos@pm.me, Pavel Luzanov , PostgreSQL Hackers References: <6d722115-6297-bc53-bb7f-5f150e765299@postgrespro.ru> <90da33e5-5886-11f0-d22f-b2ff8b2d4b99@postgrespro.ru> <163067194414.1167.8140856874411625212.pgcf@coridan.postgresql.org> <933f0fb3-5405-7160-cc8e-12f1a439c803@postgrespro.ru> From: Pavel Luzanov Message-ID: <9729572f-9ae1-23d5-8c6d-e10d6a4973a0@postgrespro.ru> Date: Tue, 7 Sep 2021 14:28:38 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------F28EF85DFD9265CBC7EC547B" Content-Language: ru List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------F28EF85DFD9265CBC7EC547B Content-Type: multipart/alternative; boundary="------------A0010F88AF671AA95BA0DECC" --------------A0010F88AF671AA95BA0DECC Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Hi, On 06.09.2021 14:39, gkokolatos@pm.me wrote: > Thanks! The tests look good. A minor nitpick would be to also add a comment on the > large object to verify that it is picked up correctly. > > Also: > > +\lo_unlink 42 > +DROP ROLE lo_test; > + > > That last empty line can be removed. The new version adds a comment to a large object and removes the last empty line. -- Pavel Luzanov Postgres Professional: https://postgrespro.com The Russian Postgres Company --------------A0010F88AF671AA95BA0DECC Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 7bit
Hi,

    
On 06.09.2021 14:39, gkokolatos@pm.me wrote:
Thanks! The tests look good. A minor nitpick would be to also add a comment on the
large object to verify that it is picked up correctly.

Also:

   +\lo_unlink 42
   +DROP ROLE lo_test;
   +

That last empty line can be removed.

    
The new version adds a comment to a large object and removes the last empty line.

    
--
Pavel Luzanov
Postgres Professional: https://postgrespro.com
The Russian Postgres Company
--------------A0010F88AF671AA95BA0DECC-- --------------F28EF85DFD9265CBC7EC547B Content-Type: text/x-patch; charset=UTF-8; name="lo-list-acl-v3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="lo-list-acl-v3.patch" diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index e0ffb020bf..374515bcb2 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -2094,7 +2094,7 @@ REVOKE ALL ON accounts FROM PUBLIC; LARGE OBJECT rw none - + \dl+ SCHEMA diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index fcab5c0d51..de47ef528e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1681,11 +1681,13 @@ testdb=> - \dl + \dl[+] This is an alias for \lo_list, which shows a - list of large objects. + list of large objects. If + is appended + to the command name, each large object is listed with its + associated permissions, if any. @@ -2588,12 +2590,15 @@ lo_import 152801 - \lo_list + \lo_list[+] Shows a list of all PostgreSQL large objects currently stored in the database, along with any comments provided for them. + If + is appended to the command name, + each large object is listed with its associated permissions, + if any. diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 49d4c0e3ce..f3645cab96 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -807,7 +807,16 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) success = describeRoles(pattern, show_verbose, show_system); break; case 'l': - success = do_lo_list(); + switch (cmd[2]) + { + case '\0': + case '+': + success = listLargeObjects(show_verbose); + break; + default: + status = PSQL_CMD_UNKNOWN; + break; + } break; case 'L': success = listLanguages(pattern, show_verbose, show_system); @@ -1901,11 +1910,13 @@ exec_command_lo(PsqlScanState scan_state, bool active_branch, const char *cmd) { char *opt1, *opt2; + bool show_verbose; opt1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); opt2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); + show_verbose = strchr(cmd, '+') ? true : false; if (strcmp(cmd + 3, "export") == 0) { @@ -1935,8 +1946,8 @@ exec_command_lo(PsqlScanState scan_state, bool active_branch, const char *cmd) } } - else if (strcmp(cmd + 3, "list") == 0) - success = do_lo_list(); + else if (strcmp(cmd + 3, "list") == 0 || strcmp(cmd + 3, "list+") == 0) + success = listLargeObjects(show_verbose); else if (strcmp(cmd + 3, "unlink") == 0) { diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 90ff649be7..a079ce9e36 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -6828,3 +6828,64 @@ listOpFamilyFunctions(const char *access_method_pattern, PQclear(res); return true; } + +/* + * \dl or \lo_list + * Lists large objects in database + */ +bool +listLargeObjects(bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + + initPQExpBuffer(&buf); + + if (pset.sversion >= 90000) + { + printfPQExpBuffer(&buf, + "SELECT oid as \"%s\",\n" + " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ", + gettext_noop("ID"), + gettext_noop("Owner")); + + if (verbose) + { + printACLColumn(&buf, "lomacl"); + appendPQExpBufferStr(&buf, ",\n "); + } + + appendPQExpBuffer(&buf, + "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" + "FROM pg_catalog.pg_largeobject_metadata\n" + "ORDER BY oid", + gettext_noop("Description")); + + } + else + { + printfPQExpBuffer(&buf, + "SELECT loid as \"%s\",\n" + " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" + "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" + "ORDER BY 1", + gettext_noop("ID"), + gettext_noop("Description")); + } + + res = PSQLexec(buf.data); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.topt.tuples_only = false; + myopt.nullPrint = NULL; + myopt.title = _("Large objects"); + myopt.translate_header = true; + + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + PQclear(res); + return true; +} diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 71b320f1fc..53738cc0cb 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -139,5 +139,8 @@ extern bool listOpFamilyOperators(const char *accessMethod_pattern, extern bool listOpFamilyFunctions(const char *access_method_pattern, const char *family_pattern, bool verbose); +/* \dl or \lo_list */ +extern bool listLargeObjects(bool verbose); + #endif /* DESCRIBE_H */ diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index d3fda67edd..7f1c9bc912 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -248,7 +248,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\dFt[+] [PATTERN] list text search templates\n")); fprintf(output, _(" \\dg[S+] [PATTERN] list roles\n")); fprintf(output, _(" \\di[S+] [PATTERN] list indexes\n")); - fprintf(output, _(" \\dl list large objects, same as \\lo_list\n")); + fprintf(output, _(" \\dl[+] list large objects, same as \\lo_list\n")); fprintf(output, _(" \\dL[S+] [PATTERN] list procedural languages\n")); fprintf(output, _(" \\dm[S+] [PATTERN] list materialized views\n")); fprintf(output, _(" \\dn[S+] [PATTERN] list schemas\n")); @@ -324,7 +324,7 @@ slashUsage(unsigned short int pager) fprintf(output, _("Large Objects\n")); fprintf(output, _(" \\lo_export LOBOID FILE\n" " \\lo_import FILE [COMMENT]\n" - " \\lo_list\n" + " \\lo_list[+]\n" " \\lo_unlink LOBOID large object operations\n")); ClosePager(output); diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index c15fcc0885..243875be83 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -262,55 +262,3 @@ do_lo_unlink(const char *loid_arg) return true; } - - - -/* - * do_lo_list() - * - * Show all large objects in database with comments - */ -bool -do_lo_list(void) -{ - PGresult *res; - char buf[1024]; - printQueryOpt myopt = pset.popt; - - if (pset.sversion >= 90000) - { - snprintf(buf, sizeof(buf), - "SELECT oid as \"%s\",\n" - " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n" - " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" - " FROM pg_catalog.pg_largeobject_metadata " - " ORDER BY oid", - gettext_noop("ID"), - gettext_noop("Owner"), - gettext_noop("Description")); - } - else - { - snprintf(buf, sizeof(buf), - "SELECT loid as \"%s\",\n" - " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" - "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" - "ORDER BY 1", - gettext_noop("ID"), - gettext_noop("Description")); - } - - res = PSQLexec(buf); - if (!res) - return false; - - myopt.topt.tuples_only = false; - myopt.nullPrint = NULL; - myopt.title = _("Large objects"); - myopt.translate_header = true; - - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); - - PQclear(res); - return true; -} diff --git a/src/bin/psql/large_obj.h b/src/bin/psql/large_obj.h index 003acbf52c..3172a7704d 100644 --- a/src/bin/psql/large_obj.h +++ b/src/bin/psql/large_obj.h @@ -11,6 +11,5 @@ bool do_lo_export(const char *loid_arg, const char *filename_arg); bool do_lo_import(const char *filename_arg, const char *comment_arg); bool do_lo_unlink(const char *loid_arg); -bool do_lo_list(void); #endif /* LARGE_OBJ_H */ diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 1b2f6bc418..00ec76d728 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -5179,3 +5179,51 @@ List of access methods pg_catalog | && | anyarray | anyarray | boolean | overlaps (1 row) +-- check printing info about large objects +-- one large object with OID=42 and owner lo_test is expected in the output +CREATE ROLE lo_test; +SELECT lo_create(42); + lo_create +----------- + 42 +(1 row) + +ALTER LARGE OBJECT 42 OWNER TO lo_test; +COMMENT ON LARGE OBJECT 42 IS 'the answer to the ultimate question of life'; +GRANT SELECT ON LARGE OBJECT 42 TO public; +\dl + Large objects + ID | Owner | Description +----+---------+--------------------------------------------- + 42 | lo_test | the answer to the ultimate question of life +(1 row) + +\dl+ + Large objects + ID | Owner | Access privileges | Description +----+---------+--------------------+--------------------------------------------- + 42 | lo_test | lo_test=rw/lo_test+| the answer to the ultimate question of life + | | =r/lo_test | +(1 row) + +\lo_list + Large objects + ID | Owner | Description +----+---------+--------------------------------------------- + 42 | lo_test | the answer to the ultimate question of life +(1 row) + +\lo_list+ + Large objects + ID | Owner | Access privileges | Description +----+---------+--------------------+--------------------------------------------- + 42 | lo_test | lo_test=rw/lo_test+| the answer to the ultimate question of life + | | =r/lo_test | +(1 row) + +\dl- +invalid command \dl- +\lo_list- +invalid command \lo_list- +\lo_unlink 42 +DROP ROLE lo_test; diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index 68121d171c..ceb1555170 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -1241,3 +1241,19 @@ drop role regress_partitioning_role; \dfa bit* small* \do - pg_catalog.int4 \do && anyarray * + +-- check printing info about large objects +-- one large object with OID=42 and owner lo_test is expected in the output +CREATE ROLE lo_test; +SELECT lo_create(42); +ALTER LARGE OBJECT 42 OWNER TO lo_test; +COMMENT ON LARGE OBJECT 42 IS 'the answer to the ultimate question of life'; +GRANT SELECT ON LARGE OBJECT 42 TO public; +\dl +\dl+ +\lo_list +\lo_list+ +\dl- +\lo_list- +\lo_unlink 42 +DROP ROLE lo_test; --------------F28EF85DFD9265CBC7EC547B--