Improve psql's \d output for partitioned indexes.
authorTom Lane
Tue, 23 Jul 2019 21:04:21 +0000 (17:04 -0400)
committerTom Lane
Tue, 23 Jul 2019 21:04:21 +0000 (17:04 -0400)
Include partitioning information much as we do for partitioned tables.
(However, \d+ doesn't show the partition bounds, because those are
not stored for indexes.)

In passing, fix a couple of queries to look less messy in -E output.

Also, add some tests for \d on tables with nondefault tablespaces.
(Somebody previously added a rather silly number of tests for \d
on partitioned indexes, yet completely neglected other cases.)

Justin Pryzby, reviewed by Fabien Coelho

Discussion: https://postgr.es/m/20190422154902[email protected]

src/bin/psql/describe.c
src/test/regress/input/tablespace.source
src/test/regress/output/tablespace.source

index b57bb8e831341131987a19cbb292eaf29169f6f7..3e59efe1c5d6bec2f8a79941b30f3206af14c129 100644 (file)
@@ -2293,8 +2293,13 @@ describeOneTableDetails(const char *schemaname,
                appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
 
            printTableAddFooter(&cont, tmpbuf.data);
-           add_tablespace_footer(&cont, tableinfo.relkind,
-                                 tableinfo.tablespace, true);
+
+           /*
+            * If it's a partitioned index, we'll print the tablespace below
+            */
+           if (tableinfo.relkind == RELKIND_INDEX)
+               add_tablespace_footer(&cont, tableinfo.relkind,
+                                     tableinfo.tablespace, true);
        }
 
        PQclear(result);
@@ -2304,6 +2309,7 @@ describeOneTableDetails(const char *schemaname,
             tableinfo.relkind == RELKIND_MATVIEW ||
             tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
             tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
+            tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
             tableinfo.relkind == RELKIND_TOASTVALUE)
    {
        /* Footer information about a table */
@@ -3070,11 +3076,17 @@ describeOneTableDetails(const char *schemaname,
        tableinfo.relkind == RELKIND_MATVIEW ||
        tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
        tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
+       tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
        tableinfo.relkind == RELKIND_TOASTVALUE)
    {
+       bool        is_partitioned;
        PGresult   *result;
        int         tuples;
 
+       /* simplify some repeated tests below */
+       is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
+                         tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
+
        /* print foreign server name */
        if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
        {
@@ -3115,13 +3127,15 @@ describeOneTableDetails(const char *schemaname,
            PQclear(result);
        }
 
-       /* print inherited tables (exclude, if parent is a partitioned table) */
+       /* print tables inherited from (exclude partitioned parents) */
        printfPQExpBuffer(&buf,
-                         "SELECT c.oid::pg_catalog.regclass"
-                         " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
-                         " WHERE c.oid=i.inhparent AND i.inhrelid = '%s'"
-                         " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
-                         " ORDER BY inhseqno;", oid);
+                         "SELECT c.oid::pg_catalog.regclass\n"
+                         "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
+                         "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
+                         "  AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
+                         " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
+                         "\nORDER BY inhseqno;",
+                         oid);
 
        result = PSQLexec(buf.data);
        if (!result)
@@ -3153,31 +3167,32 @@ describeOneTableDetails(const char *schemaname,
        /* print child tables (with additional info if partitions) */
        if (pset.sversion >= 100000)
            printfPQExpBuffer(&buf,
-                             "SELECT c.oid::pg_catalog.regclass,"
-                             "       pg_catalog.pg_get_expr(c.relpartbound, c.oid),"
-                             "       c.relkind"
-                             " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
-                             " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'"
-                             " ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
-                             "          c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
+                             "SELECT c.oid::pg_catalog.regclass, c.relkind,"
+                             " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
+                             "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
+                             "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
+                             "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
+                             " c.oid::pg_catalog.regclass::pg_catalog.text;",
+                             oid);
        else if (pset.sversion >= 80300)
            printfPQExpBuffer(&buf,
-                             "SELECT c.oid::pg_catalog.regclass"
-                             " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
-                             " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'"
-                             " ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
+                             "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n"
+                             "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
+                             "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
+                             "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
+                             oid);
        else
            printfPQExpBuffer(&buf,
-                             "SELECT c.oid::pg_catalog.regclass"
-                             " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
-                             " WHERE c.oid=i.inhrelid AND i.inhparent = '%s'"
-                             " ORDER BY c.relname;", oid);
+                             "SELECT c.oid::pg_catalog.regclass, c.relkind, NULL\n"
+                             "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
+                             "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
+                             "ORDER BY c.relname;",
+                             oid);
 
        result = PSQLexec(buf.data);
        if (!result)
            goto error_return;
-       else
-           tuples = PQntuples(result);
+       tuples = PQntuples(result);
 
        /*
         * For a partitioned table with no partitions, always print the number
@@ -3185,7 +3200,7 @@ describeOneTableDetails(const char *schemaname,
         * Otherwise, we will not print "Partitions" section for a partitioned
         * table without any partitions.
         */
-       if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE && tuples == 0)
+       if (is_partitioned && tuples == 0)
        {
            printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
            printTableAddFooter(&cont, buf.data);
@@ -3195,49 +3210,34 @@ describeOneTableDetails(const char *schemaname,
            /* print the number of child tables, if any */
            if (tuples > 0)
            {
-               if (tableinfo.relkind != RELKIND_PARTITIONED_TABLE)
-                   printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
-               else
+               if (is_partitioned)
                    printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
+               else
+                   printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
                printTableAddFooter(&cont, buf.data);
            }
        }
        else
        {
            /* display the list of child tables */
-           const char *ct = (tableinfo.relkind != RELKIND_PARTITIONED_TABLE) ?
-           _("Child tables") : _("Partitions");
+           const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
            int         ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
 
            for (i = 0; i < tuples; i++)
            {
-               if (tableinfo.relkind != RELKIND_PARTITIONED_TABLE)
-               {
-                   if (i == 0)
-                       printfPQExpBuffer(&buf, "%s: %s",
-                                         ct, PQgetvalue(result, i, 0));
-                   else
-                       printfPQExpBuffer(&buf, "%*s  %s",
-                                         ctw, "", PQgetvalue(result, i, 0));
-               }
-               else
-               {
-                   char       *partitioned_note;
-
-                   if (*PQgetvalue(result, i, 2) == RELKIND_PARTITIONED_TABLE)
-                       partitioned_note = ", PARTITIONED";
-                   else
-                       partitioned_note = "";
+               char        child_relkind = *PQgetvalue(result, i, 1);
 
-                   if (i == 0)
-                       printfPQExpBuffer(&buf, "%s: %s %s%s",
-                                         ct, PQgetvalue(result, i, 0), PQgetvalue(result, i, 1),
-                                         partitioned_note);
-                   else
-                       printfPQExpBuffer(&buf, "%*s  %s %s%s",
-                                         ctw, "", PQgetvalue(result, i, 0), PQgetvalue(result, i, 1),
-                                         partitioned_note);
-               }
+               if (i == 0)
+                   printfPQExpBuffer(&buf, "%s: %s",
+                                     ct, PQgetvalue(result, i, 0));
+               else
+                   printfPQExpBuffer(&buf, "%*s  %s",
+                                     ctw, "", PQgetvalue(result, i, 0));
+               if (!PQgetisnull(result, i, 2))
+                   appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 2));
+               if (child_relkind == RELKIND_PARTITIONED_TABLE ||
+                   child_relkind == RELKIND_PARTITIONED_INDEX)
+                   appendPQExpBufferStr(&buf, ", PARTITIONED");
                if (i < tuples - 1)
                    appendPQExpBufferChar(&buf, ',');
 
index 8f012fcc339b3c1fda59d6fd47bfbf5a6095dea8..a5f61a35dc59fbd95222233abf9902955f4ea2f0 100644 (file)
@@ -44,6 +44,10 @@ CREATE INDEX foo_idx on testschema.foo(i) TABLESPACE regress_tblspace;
 SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
     where c.reltablespace = t.oid AND c.relname = 'foo_idx';
 
+-- check \d output
+\d testschema.foo
+\d testschema.foo_idx
+
 --
 -- partitioned table
 --
@@ -85,7 +89,12 @@ CREATE INDEX part_a_idx ON testschema.part (a) TABLESPACE regress_tblspace;
 CREATE TABLE testschema.part2 PARTITION OF testschema.part FOR VALUES IN (2);
 SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
     where c.reltablespace = t.oid AND c.relname LIKE 'part%_idx';
+\d testschema.part
+\d+ testschema.part
+\d testschema.part1
+\d+ testschema.part1
 \d testschema.part_a_idx
+\d+ testschema.part_a_idx
 
 -- partitioned rels cannot specify the default tablespace.  These fail:
 CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE pg_default;
index 2ea68cabb0a3bc1a13d1cba64f32c2d5030bea5b..162b591b315f3f67e5223cbab478d16f72b7cd75 100644 (file)
@@ -61,6 +61,24 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
  foo_idx | regress_tblspace
 (1 row)
 
+-- check \d output
+\d testschema.foo
+              Table "testschema.foo"
+ Column |  Type   | Collation | Nullable | Default 
+--------+---------+-----------+----------+---------
+ i      | integer |           |          | 
+Indexes:
+    "foo_idx" btree (i), tablespace "regress_tblspace"
+Tablespace: "regress_tblspace"
+
+\d testschema.foo_idx
+      Index "testschema.foo_idx"
+ Column |  Type   | Key? | Definition 
+--------+---------+------+------------
+ i      | integer | yes  | i
+btree, for table "testschema.foo"
+Tablespace: "regress_tblspace"
+
 --
 -- partitioned table
 --
@@ -122,12 +140,63 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
  part_a_idx  | regress_tblspace
 (3 rows)
 
+\d testschema.part
+        Partitioned table "testschema.part"
+ Column |  Type   | Collation | Nullable | Default 
+--------+---------+-----------+----------+---------
+ a      | integer |           |          | 
+Partition key: LIST (a)
+Indexes:
+    "part_a_idx" btree (a), tablespace "regress_tblspace"
+Number of partitions: 2 (Use \d+ to list them.)
+
+\d+ testschema.part
+                           Partitioned table "testschema.part"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+Partition key: LIST (a)
+Indexes:
+    "part_a_idx" btree (a), tablespace "regress_tblspace"
+Partitions: testschema.part1 FOR VALUES IN (1),
+            testschema.part2 FOR VALUES IN (2)
+
+\d testschema.part1
+             Table "testschema.part1"
+ Column |  Type   | Collation | Nullable | Default 
+--------+---------+-----------+----------+---------
+ a      | integer |           |          | 
+Partition of: testschema.part FOR VALUES IN (1)
+Indexes:
+    "part1_a_idx" btree (a), tablespace "regress_tblspace"
+
+\d+ testschema.part1
+                                 Table "testschema.part1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+Partition of: testschema.part FOR VALUES IN (1)
+Partition constraint: ((a IS NOT NULL) AND (a = 1))
+Indexes:
+    "part1_a_idx" btree (a), tablespace "regress_tblspace"
+
 \d testschema.part_a_idx
 Partitioned index "testschema.part_a_idx"
  Column |  Type   | Key? | Definition 
 --------+---------+------+------------
  a      | integer | yes  | a
 btree, for table "testschema.part"
+Number of partitions: 2 (Use \d+ to list them.)
+Tablespace: "regress_tblspace"
+
+\d+ testschema.part_a_idx
+           Partitioned index "testschema.part_a_idx"
+ Column |  Type   | Key? | Definition | Storage | Stats target 
+--------+---------+------+------------+---------+--------------
+ a      | integer | yes  | a          | plain   | 
+btree, for table "testschema.part"
+Partitions: testschema.part1_a_idx,
+            testschema.part2_a_idx
 Tablespace: "regress_tblspace"
 
 -- partitioned rels cannot specify the default tablespace.  These fail:
@@ -344,6 +413,7 @@ Partitioned index "testschema.test_index1"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index2
 Partitioned index "testschema.test_index2"
@@ -351,6 +421,7 @@ Partitioned index "testschema.test_index2"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 \d testschema.test_index3
@@ -359,6 +430,7 @@ Partitioned index "testschema.test_index3"
 --------+--------+------+------------
  id     | bigint | yes  | id
 primary key, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index4
 Partitioned index "testschema.test_index4"
@@ -366,6 +438,7 @@ Partitioned index "testschema.test_index4"
 --------+--------+------+------------
  id     | bigint | yes  | id
 unique, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 -- use a custom tablespace for default_tablespace
@@ -378,6 +451,7 @@ Partitioned index "testschema.test_index1"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index2
 Partitioned index "testschema.test_index2"
@@ -385,6 +459,7 @@ Partitioned index "testschema.test_index2"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 \d testschema.test_index3
@@ -393,6 +468,7 @@ Partitioned index "testschema.test_index3"
 --------+--------+------+------------
  id     | bigint | yes  | id
 primary key, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index4
 Partitioned index "testschema.test_index4"
@@ -400,6 +476,7 @@ Partitioned index "testschema.test_index4"
 --------+--------+------+------------
  id     | bigint | yes  | id
 unique, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 SELECT * FROM testschema.test_default_tab_p;
@@ -416,6 +493,7 @@ Partitioned index "testschema.test_index1"
 --------+---------+------+------------
  val    | integer | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index2
 Partitioned index "testschema.test_index2"
@@ -423,6 +501,7 @@ Partitioned index "testschema.test_index2"
 --------+---------+------+------------
  val    | integer | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 \d testschema.test_index3
@@ -431,6 +510,7 @@ Partitioned index "testschema.test_index3"
 --------+--------+------+------------
  id     | bigint | yes  | id
 primary key, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index4
 Partitioned index "testschema.test_index4"
@@ -438,6 +518,7 @@ Partitioned index "testschema.test_index4"
 --------+--------+------+------------
  id     | bigint | yes  | id
 unique, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 SELECT * FROM testschema.test_default_tab_p;
@@ -456,6 +537,7 @@ Partitioned index "testschema.test_index1"
 --------+---------+------+------------
  val    | integer | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index2
 Partitioned index "testschema.test_index2"
@@ -463,6 +545,7 @@ Partitioned index "testschema.test_index2"
 --------+---------+------+------------
  val    | integer | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 \d testschema.test_index3
@@ -471,6 +554,7 @@ Partitioned index "testschema.test_index3"
 --------+--------+------+------------
  id     | bigint | yes  | id
 primary key, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index4
 Partitioned index "testschema.test_index4"
@@ -478,6 +562,7 @@ Partitioned index "testschema.test_index4"
 --------+--------+------+------------
  id     | bigint | yes  | id
 unique, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 -- tablespace should not change even if there is an index rewrite
@@ -488,6 +573,7 @@ Partitioned index "testschema.test_index1"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index2
 Partitioned index "testschema.test_index2"
@@ -495,6 +581,7 @@ Partitioned index "testschema.test_index2"
 --------+--------+------+------------
  val    | bigint | yes  | val
 btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 \d testschema.test_index3
@@ -503,6 +590,7 @@ Partitioned index "testschema.test_index3"
 --------+--------+------+------------
  id     | bigint | yes  | id
 primary key, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 
 \d testschema.test_index4
 Partitioned index "testschema.test_index4"
@@ -510,6 +598,7 @@ Partitioned index "testschema.test_index4"
 --------+--------+------+------------
  id     | bigint | yes  | id
 unique, btree, for table "testschema.test_default_tab_p"
+Number of partitions: 1 (Use \d+ to list them.)
 Tablespace: "regress_tblspace"
 
 DROP TABLE testschema.test_default_tab_p;