Extend psql's \lo_list/\dl to be able to print large objects' ACLs.
authorTom Lane
Thu, 6 Jan 2022 18:09:05 +0000 (13:09 -0500)
committerTom Lane
Thu, 6 Jan 2022 18:09:05 +0000 (13:09 -0500)
The ACL is printed when you add + to the command, similarly to
various other psql backslash commands.

Along the way, move the code for this into describe.c,
where it is a better fit (and can share some code).

Pavel Luzanov, reviewed by Georgios Kokolatos

Discussion: https://postgr.es/m/6d722115-6297-bc53-bb7f-5f150e765299@postgrespro.ru

doc/src/sgml/ddl.sgml
doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/bin/psql/large_obj.c
src/bin/psql/large_obj.h
src/test/regress/expected/largeobject.out
src/test/regress/expected/largeobject_1.out
src/test/regress/sql/largeobject.sql

index 64d9030652288e0409d92957ac9d38045935e7b4..22f6c5c7abc09902f8c7349e2606e233ca20e8f3 100644 (file)
@@ -2146,7 +2146,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
       LARGE OBJECT
       rw
       none
-      
+      <literal>\dl+</entry>
      
      
       SCHEMA
index ae38d3dcc3ee71a434a6899bdb7b2f39f8cc77a7..1ab200a4adc26deaa53fe119f31e4ca9d72750b7 100644 (file)
@@ -1681,11 +1681,14 @@ testdb=>
 
 
       
-        \dl
+        \dl[+]
         
         
         This is an alias for \lo_list, which shows a
         list of large objects.
+        If + is appended to the command name,
+        each large object is listed with its associated permissions,
+        if any.
         
         
       
@@ -2610,12 +2613,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.
         
         
       
index fb3bab949487393dc33d0ecfff1d82ad92ad64e1..414fc32152ab9067553802375605fa584d779175 100644 (file)
@@ -811,7 +811,7 @@ 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();
+               success = listLargeObjects(show_verbose);
                break;
            case 'L':
                success = listLanguages(pattern, show_verbose, show_system);
@@ -1963,7 +1963,9 @@ exec_command_lo(PsqlScanState scan_state, bool active_branch, const char *cmd)
        }
 
        else if (strcmp(cmd + 3, "list") == 0)
-           success = do_lo_list();
+           success = listLargeObjects(false);
+       else if (strcmp(cmd + 3, "list+") == 0)
+           success = listLargeObjects(true);
 
        else if (strcmp(cmd + 3, "unlink") == 0)
        {
index 0615de53255fe9ee935997e79796ebbc1fab1c99..cdb33719ffa5756edd5f38940ae1763cb57e750a 100644 (file)
@@ -6463,3 +6463,49 @@ listOpFamilyFunctions(const char *access_method_pattern,
    PQclear(res);
    return true;
 }
+
+/*
+ * \dl or \lo_list
+ * Lists large objects
+ */
+bool
+listLargeObjects(bool verbose)
+{
+   PQExpBufferData buf;
+   PGresult   *res;
+   printQueryOpt myopt = pset.popt;
+
+   initPQExpBuffer(&buf);
+
+   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"));
+
+   res = PSQLexec(buf.data);
+   termPQExpBuffer(&buf);
+   if (!res)
+       return false;
+
+   myopt.nullPrint = NULL;
+   myopt.title = _("Large objects");
+   myopt.translate_header = true;
+
+   printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+   PQclear(res);
+   return true;
+}
index 71b320f1fc6922cb2e6b2d17005659223395c55e..b57ba67bba22e0b9458036df8421453ecd4d2762 100644 (file)
@@ -139,5 +139,7 @@ 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 */
index 8cadfbb1032aade4409090f89a7a6afee05cb380..5752a36ac82d1534b07a975d5208a1707ac8541d 100644 (file)
@@ -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"));
@@ -325,7 +325,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);
index 10e47c87ac9f0326d318b80e1e7e9305417e1856..243875be83de9d5e6bd9498f11dac9d49932a5db 100644 (file)
@@ -262,42 +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;
-
-   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"));
-
-   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;
-}
index 003acbf52c9d88c19899a6698d4f4920a6861b71..3172a7704d06d2f4477c334381ff901f6ccb00ec 100644 (file)
@@ -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 */
index f461ca3b9f4946f2a4cfe4700115f883b6d95843..8e32de04e640fa12c9741e19c4366aea14fe7a4c 100644 (file)
@@ -6,31 +6,46 @@
 \getenv abs_builddir PG_ABS_BUILDDIR
 -- ensure consistent test output regardless of the default bytea format
 SET bytea_output TO escape;
+-- Test ALTER LARGE OBJECT OWNER, GRANT, COMMENT
+CREATE ROLE regress_lo_user;
+SELECT lo_create(42);
+ lo_create 
+-----------
+        42
+(1 row)
+
+ALTER LARGE OBJECT 42 OWNER TO regress_lo_user;
+GRANT SELECT ON LARGE OBJECT 42 TO public;
+COMMENT ON LARGE OBJECT 42 IS 'the ultimate answer';
+-- Test psql's \lo_list et al (we assume no other LOs exist yet)
+\lo_list
+               Large objects
+ ID |      Owner      |     Description     
+----+-----------------+---------------------
+ 42 | regress_lo_user | the ultimate answer
+(1 row)
+
+\lo_list+
+                                  Large objects
+ ID |      Owner      |         Access privileges          |     Description     
+----+-----------------+------------------------------------+---------------------
+ 42 | regress_lo_user | regress_lo_user=rw/regress_lo_user+| the ultimate answer
+    |                 | =r/regress_lo_user                 | 
+(1 row)
+
+\lo_unlink 42
+\dl
+      Large objects
+ ID | Owner | Description 
+----+-------+-------------
+(0 rows)
+
 -- Load a file
 CREATE TABLE lotest_stash_values (loid oid, fd integer);
 -- lo_creat(mode integer) returns oid
 -- The mode arg to lo_creat is unused, some vestigal holdover from ancient times
 -- returns the large object id
 INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42);
--- Test ALTER LARGE OBJECT
-CREATE ROLE regress_lo_user;
-DO $$
-  BEGIN
-    EXECUTE 'ALTER LARGE OBJECT ' || (select loid from lotest_stash_values)
-       || ' OWNER TO regress_lo_user';
-  END
-$$;
-SELECT
-   rol.rolname
-FROM
-   lotest_stash_values s
-   JOIN pg_largeobject_metadata lo ON s.loid = lo.oid
-   JOIN pg_authid rol ON lo.lomowner = rol.oid;
-     rolname     
------------------
- regress_lo_user
-(1 row)
-
 -- NOTE: large objects require transactions
 BEGIN;
 -- lo_open(lobjId oid, mode integer) returns integer
index a9725c375d59e55ed8f841876db0e011e9580eb0..561065da58613fec2d31e7c2e0adcd435fc3b298 100644 (file)
@@ -6,31 +6,46 @@
 \getenv abs_builddir PG_ABS_BUILDDIR
 -- ensure consistent test output regardless of the default bytea format
 SET bytea_output TO escape;
+-- Test ALTER LARGE OBJECT OWNER, GRANT, COMMENT
+CREATE ROLE regress_lo_user;
+SELECT lo_create(42);
+ lo_create 
+-----------
+        42
+(1 row)
+
+ALTER LARGE OBJECT 42 OWNER TO regress_lo_user;
+GRANT SELECT ON LARGE OBJECT 42 TO public;
+COMMENT ON LARGE OBJECT 42 IS 'the ultimate answer';
+-- Test psql's \lo_list et al (we assume no other LOs exist yet)
+\lo_list
+               Large objects
+ ID |      Owner      |     Description     
+----+-----------------+---------------------
+ 42 | regress_lo_user | the ultimate answer
+(1 row)
+
+\lo_list+
+                                  Large objects
+ ID |      Owner      |         Access privileges          |     Description     
+----+-----------------+------------------------------------+---------------------
+ 42 | regress_lo_user | regress_lo_user=rw/regress_lo_user+| the ultimate answer
+    |                 | =r/regress_lo_user                 | 
+(1 row)
+
+\lo_unlink 42
+\dl
+      Large objects
+ ID | Owner | Description 
+----+-------+-------------
+(0 rows)
+
 -- Load a file
 CREATE TABLE lotest_stash_values (loid oid, fd integer);
 -- lo_creat(mode integer) returns oid
 -- The mode arg to lo_creat is unused, some vestigal holdover from ancient times
 -- returns the large object id
 INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42);
--- Test ALTER LARGE OBJECT
-CREATE ROLE regress_lo_user;
-DO $$
-  BEGIN
-    EXECUTE 'ALTER LARGE OBJECT ' || (select loid from lotest_stash_values)
-       || ' OWNER TO regress_lo_user';
-  END
-$$;
-SELECT
-   rol.rolname
-FROM
-   lotest_stash_values s
-   JOIN pg_largeobject_metadata lo ON s.loid = lo.oid
-   JOIN pg_authid rol ON lo.lomowner = rol.oid;
-     rolname     
------------------
- regress_lo_user
-(1 row)
-
 -- NOTE: large objects require transactions
 BEGIN;
 -- lo_open(lobjId oid, mode integer) returns integer
index 16da077f3a2dfbb8953efc087ef5bd21477032dd..fd3518889ebe1bb59ce14f8976976530d2cabcee 100644 (file)
@@ -9,6 +9,19 @@
 -- ensure consistent test output regardless of the default bytea format
 SET bytea_output TO escape;
 
+-- Test ALTER LARGE OBJECT OWNER, GRANT, COMMENT
+CREATE ROLE regress_lo_user;
+SELECT lo_create(42);
+ALTER LARGE OBJECT 42 OWNER TO regress_lo_user;
+GRANT SELECT ON LARGE OBJECT 42 TO public;
+COMMENT ON LARGE OBJECT 42 IS 'the ultimate answer';
+
+-- Test psql's \lo_list et al (we assume no other LOs exist yet)
+\lo_list
+\lo_list+
+\lo_unlink 42
+\dl
+
 -- Load a file
 CREATE TABLE lotest_stash_values (loid oid, fd integer);
 -- lo_creat(mode integer) returns oid
@@ -16,21 +29,6 @@ CREATE TABLE lotest_stash_values (loid oid, fd integer);
 -- returns the large object id
 INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42);
 
--- Test ALTER LARGE OBJECT
-CREATE ROLE regress_lo_user;
-DO $$
-  BEGIN
-    EXECUTE 'ALTER LARGE OBJECT ' || (select loid from lotest_stash_values)
-       || ' OWNER TO regress_lo_user';
-  END
-$$;
-SELECT
-   rol.rolname
-FROM
-   lotest_stash_values s
-   JOIN pg_largeobject_metadata lo ON s.loid = lo.oid
-   JOIN pg_authid rol ON lo.lomowner = rol.oid;
-
 -- NOTE: large objects require transactions
 BEGIN;