psql: Update \d sequence display
authorPeter Eisentraut <peter_e@gmx.net>
Mon, 25 Sep 2017 15:59:46 +0000 (11:59 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 29 Sep 2017 17:37:30 +0000 (13:37 -0400)
For \d sequencename, the psql code just did SELECT * FROM sequencename
to get the information to display, but this does not contain much
interesting information anymore in PostgreSQL 10, because the metadata
has been moved to a separate system catalog.

This patch creates a newly designed sequence display that is not merely
an extension of the general relation/table display as it was previously.

Example:

PostgreSQL 9.6:

=> \d foobar
           Sequence "public.foobar"
    Column     |  Type   |        Value
---------------+---------+---------------------
 sequence_name | name    | foobar
 last_value    | bigint  | 1
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 0
 is_cycled     | boolean | f
 is_called     | boolean | f

PostgreSQL 10 before this change:

=> \d foobar
   Sequence "public.foobar"
   Column   |  Type   | Value
------------+---------+-------
 last_value | bigint  | 1
 log_cnt    | bigint  | 0
 is_called  | boolean | f

New:

=> \d foobar
                           Sequence "public.foobar"
  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
 bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1

Reviewed-by: Fabien COELHO <coelho@cri.ensmp.fr>
src/bin/psql/describe.c
src/test/regress/expected/identity.out
src/test/regress/expected/sequence.out
src/test/regress/sql/identity.sql
src/test/regress/sql/sequence.sql

index d22ec68431e231d9c781c2256a6030d66e0fd09d..a3209b73fa225327d82fe6596521168f7fc4201c 100644 (file)
@@ -1380,8 +1380,6 @@ describeOneTableDetails(const char *schemaname,
        int                     i;
        char       *view_def = NULL;
        char       *headers[11];
-       char      **seq_values = NULL;
-       char      **ptr;
        PQExpBufferData title;
        PQExpBufferData tmpbuf;
        int                     cols;
@@ -1563,27 +1561,125 @@ describeOneTableDetails(const char *schemaname,
        res = NULL;
 
        /*
-        * If it's a sequence, fetch its values and store into an array that will
-        * be used later.
+        * If it's a sequence, deal with it here separately.
         */
        if (tableinfo.relkind == RELKIND_SEQUENCE)
        {
-               printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-               /* must be separate because fmtId isn't reentrant */
-               appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+               PGresult   *result = NULL;
+               printQueryOpt myopt = pset.popt;
+               char       *footers[2] = {NULL, NULL};
+
+               if (pset.sversion >= 100000)
+               {
+                       printfPQExpBuffer(&buf,
+                                                         "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
+                                                         "       seqstart AS \"%s\",\n"
+                                                         "       seqmin AS \"%s\",\n"
+                                                         "       seqmax AS \"%s\",\n"
+                                                         "       seqincrement AS \"%s\",\n"
+                                                         "       CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
+                                                         "       seqcache AS \"%s\"\n",
+                                                         gettext_noop("Type"),
+                                                         gettext_noop("Start"),
+                                                         gettext_noop("Minimum"),
+                                                         gettext_noop("Maximum"),
+                                                         gettext_noop("Increment"),
+                                                         gettext_noop("yes"),
+                                                         gettext_noop("no"),
+                                                         gettext_noop("Cycles?"),
+                                                         gettext_noop("Cache"));
+                       appendPQExpBuffer(&buf,
+                                                         "FROM pg_catalog.pg_sequence\n"
+                                                         "WHERE seqrelid = '%s';",
+                                                         oid);
+               }
+               else
+               {
+                       printfPQExpBuffer(&buf,
+                                                         "SELECT pg_catalog.format_type('bigint'::regtype, NULL) AS \"%s\",\n"
+                                                         "       start_value AS \"%s\",\n"
+                                                         "       min_value AS \"%s\",\n"
+                                                         "       max_value AS \"%s\",\n"
+                                                         "       increment_by AS \"%s\",\n"
+                                                         "       CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
+                                                         "       cache_value AS \"%s\"\n",
+                                                         gettext_noop("Type"),
+                                                         gettext_noop("Start"),
+                                                         gettext_noop("Minimum"),
+                                                         gettext_noop("Maximum"),
+                                                         gettext_noop("Increment"),
+                                                         gettext_noop("yes"),
+                                                         gettext_noop("no"),
+                                                         gettext_noop("Cycles?"),
+                                                         gettext_noop("Cache"));
+                       appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
+                       /* must be separate because fmtId isn't reentrant */
+                       appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+               }
 
                res = PSQLexec(buf.data);
                if (!res)
                        goto error_return;
 
-               seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
+               /* Footer information about a sequence */
+
+               /* Get the column that owns this sequence */
+               printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
+                                                 "\n   pg_catalog.quote_ident(relname) || '.' ||"
+                                                 "\n   pg_catalog.quote_ident(attname),"
+                                                 "\n   d.deptype"
+                                                 "\nFROM pg_catalog.pg_class c"
+                                                 "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
+                                                 "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
+                                                 "\nINNER JOIN pg_catalog.pg_attribute a ON ("
+                                                 "\n a.attrelid=c.oid AND"
+                                                 "\n a.attnum=d.refobjsubid)"
+                                                 "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
+                                                 "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
+                                                 "\n AND d.objid='%s'"
+                                                 "\n AND d.deptype IN ('a', 'i')",
+                                                 oid);
+
+               result = PSQLexec(buf.data);
+
+               /*
+                * If we get no rows back, don't show anything (obviously). We should
+                * never get more than one row back, but if we do, just ignore it and
+                * don't print anything.
+                */
+               if (!result)
+                       goto error_return;
+               else if (PQntuples(result) == 1)
+               {
+                       switch (PQgetvalue(result, 0, 1)[0])
+                       {
+                               case 'a':
+                                       footers[0] = psprintf(_("Owned by: %s"),
+                                                                                 PQgetvalue(result, 0, 0));
+                                       break;
+                               case 'i':
+                                       footers[0] = psprintf(_("Sequence for identity column: %s"),
+                                                                                 PQgetvalue(result, 0, 0));
+                                       break;
+                       }
+               }
+               PQclear(result);
+
+               printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
+                                                 schemaname, relationname);
 
-               for (i = 0; i < PQnfields(res); i++)
-                       seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-               seq_values[i] = NULL;
+               myopt.footers = footers;
+               myopt.topt.default_footer = false;
+               myopt.title = title.data;
+               myopt.translate_header = true;
 
-               PQclear(res);
-               res = NULL;
+               printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+               if (footers[0])
+                       free(footers[0]);
+
+               retval = true;
+               goto error_return;              /* not an error, just return early */
        }
 
        /*
@@ -1667,10 +1763,6 @@ describeOneTableDetails(const char *schemaname,
                                printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
                                                                  schemaname, relationname);
                        break;
-               case RELKIND_SEQUENCE:
-                       printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
-                                                         schemaname, relationname);
-                       break;
                case RELKIND_INDEX:
                        if (tableinfo.relpersistence == 'u')
                                printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
@@ -1729,9 +1821,6 @@ describeOneTableDetails(const char *schemaname,
                show_column_details = true;
        }
 
-       if (tableinfo.relkind == RELKIND_SEQUENCE)
-               headers[cols++] = gettext_noop("Value");
-
        if (tableinfo.relkind == RELKIND_INDEX)
                headers[cols++] = gettext_noop("Definition");
 
@@ -1814,10 +1903,6 @@ describeOneTableDetails(const char *schemaname,
                        printTableAddCell(&cont, default_str, false, false);
                }
 
-               /* Value: for sequences only */
-               if (tableinfo.relkind == RELKIND_SEQUENCE)
-                       printTableAddCell(&cont, seq_values[i], false, false);
-
                /* Expression for index column */
                if (tableinfo.relkind == RELKIND_INDEX)
                        printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
@@ -2030,55 +2115,6 @@ describeOneTableDetails(const char *schemaname,
 
                PQclear(result);
        }
-       else if (tableinfo.relkind == RELKIND_SEQUENCE)
-       {
-               /* Footer information about a sequence */
-               PGresult   *result = NULL;
-
-               /* Get the column that owns this sequence */
-               printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
-                                                 "\n   pg_catalog.quote_ident(relname) || '.' ||"
-                                                 "\n   pg_catalog.quote_ident(attname),"
-                                                 "\n   d.deptype"
-                                                 "\nFROM pg_catalog.pg_class c"
-                                                 "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
-                                                 "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
-                                                 "\nINNER JOIN pg_catalog.pg_attribute a ON ("
-                                                 "\n a.attrelid=c.oid AND"
-                                                 "\n a.attnum=d.refobjsubid)"
-                                                 "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
-                                                 "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
-                                                 "\n AND d.objid='%s'"
-                                                 "\n AND d.deptype IN ('a', 'i')",
-                                                 oid);
-
-               result = PSQLexec(buf.data);
-               if (!result)
-                       goto error_return;
-               else if (PQntuples(result) == 1)
-               {
-                       switch (PQgetvalue(result, 0, 1)[0])
-                       {
-                               case 'a':
-                                       printfPQExpBuffer(&buf, _("Owned by: %s"),
-                                                                         PQgetvalue(result, 0, 0));
-                                       printTableAddFooter(&cont, buf.data);
-                                       break;
-                               case 'i':
-                                       printfPQExpBuffer(&buf, _("Sequence for identity column: %s"),
-                                                                         PQgetvalue(result, 0, 0));
-                                       printTableAddFooter(&cont, buf.data);
-                                       break;
-                       }
-               }
-
-               /*
-                * If we get no rows back, don't show anything (obviously). We should
-                * never get more than one row back, but if we do, just ignore it and
-                * don't print anything.
-                */
-               PQclear(result);
-       }
        else if (tableinfo.relkind == RELKIND_RELATION ||
                         tableinfo.relkind == RELKIND_MATVIEW ||
                         tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
@@ -2963,13 +2999,6 @@ error_return:
        termPQExpBuffer(&title);
        termPQExpBuffer(&tmpbuf);
 
-       if (seq_values)
-       {
-               for (ptr = seq_values; *ptr; ptr++)
-                       free(*ptr);
-               free(seq_values);
-       }
-
        if (view_def)
                free(view_def);
 
index 2800ed7caabc3ecf821ec75066cea5482b26b0d3..5fa585d6cc55b69ec421be1063813616291a43dc 100644 (file)
@@ -32,6 +32,13 @@ SELECT pg_get_serial_sequence('itest1', 'a');
  public.itest1_a_seq
 (1 row)
 
+\d itest1_a_seq
+                    Sequence "public.itest1_a_seq"
+  Type   | Start | Minimum |  Maximum   | Increment | Cycles? | Cache 
+---------+-------+---------+------------+-----------+---------+-------
+ integer |     1 |       1 | 2147483647 |         1 | no      |     1
+Sequence for identity column: public.itest1.a
+
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ERROR:  column "a" of relation "itest4" must be declared NOT NULL before identity can be added
index ea05a3382b8766a90ca155128bfc5f005b0c8075..2384b7dd815942189ce1c56de937c480181c0a79 100644 (file)
@@ -535,6 +535,19 @@ SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass);
           -1 | -9223372036854775808 |            -1 |        -1 | f            |          1 |        20
 (1 row)
 
+\d sequence_test4
+                       Sequence "public.sequence_test4"
+  Type  | Start |       Minimum        | Maximum | Increment | Cycles? | Cache 
+--------+-------+----------------------+---------+-----------+---------+-------
+ bigint |    -1 | -9223372036854775808 |      -1 |        -1 | no      |     1
+
+\d serialtest2_f2_seq
+                 Sequence "public.serialtest2_f2_seq"
+  Type   | Start | Minimum |  Maximum   | Increment | Cycles? | Cache 
+---------+-------+---------+------------+-----------+---------+-------
+ integer |     1 |       1 | 2147483647 |         1 | no      |     1
+Owned by: public.serialtest2.f2
+
 -- Test comments
 COMMENT ON SEQUENCE asdf IS 'won''t work';
 ERROR:  relation "asdf" does not exist
index 7886456a56974fab1173d4914966c22b88342651..e1b5a074c96102f3e1288325ae9a14c3acc90e50 100644 (file)
@@ -14,6 +14,8 @@ SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE
 
 SELECT pg_get_serial_sequence('itest1', 'a');
 
+\d itest1_a_seq
+
 CREATE TABLE itest4 (a int, b text);
 ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;  -- error, requires NOT NULL
 ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
index c50834a5b97e61f3f2246b5029769078aa12368c..a7b9e633728bdf345723e37f0119afde8d306675 100644 (file)
@@ -246,6 +246,10 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest'])
 SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass);
 
 
+\d sequence_test4
+\d serialtest2_f2_seq
+
+
 -- Test comments
 COMMENT ON SEQUENCE asdf IS 'won''t work';
 COMMENT ON SEQUENCE sequence_test2 IS 'will work';