ToDo: show size of partitioned table

Started by Pavel Stehuleover 7 years ago106 messages
#1Pavel Stehule
pavel.stehule@gmail.com

Hi

postgres=# SELECT count(*) from data;
┌─────────┐
│ count │
╞═════════╡
│ 1000000 │
└─────────┘
(1 row)

\dt+ can display actual size of partitioned table data - now zero is
displayed

postgres=# \dt+ data
List of relations
┌────────┬──────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
└────────┴──────┴───────┴───────┴─────────┴─────────────┘
(1 row)

postgres=# \dt+ data*
List of relations
┌────────┬────────────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪════════════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
│ public │ data_2016 │ table │ pavel │ 17 MB │ │
│ public │ data_2017 │ table │ pavel │ 17 MB │ │
│ public │ data_other │ table │ pavel │ 8224 kB │ │
└────────┴────────────┴───────┴───────┴─────────┴─────────────┘
(4 rows)

or we can introduce some option for display only partitioned tables without
partitions.

Regards

Pavel

#2Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Pavel Stehule (#1)
Re: ToDo: show size of partitioned table

On Fri, Jun 1, 2018 at 12:56 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

Hi

postgres=# SELECT count(*) from data;
┌─────────┐
│ count │
╞═════════╡
│ 1000000 │
└─────────┘
(1 row)

\dt+ can display actual size of partitioned table data - now zero is
displayed

postgres=# \dt+ data
List of relations
┌────────┬──────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
└────────┴──────┴───────┴───────┴─────────┴─────────────┘
(1 row)

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

#3Pavel Stehule
pavel.stehule@gmail.com
In reply to: Ashutosh Bapat (#2)
Re: ToDo: show size of partitioned table

2018-06-01 17:15 GMT+02:00 Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>:

On Fri, Jun 1, 2018 at 12:56 AM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Hi

postgres=# SELECT count(*) from data;
┌─────────┐
│ count │
╞═════════╡
│ 1000000 │
└─────────┘
(1 row)

\dt+ can display actual size of partitioned table data - now zero is
displayed

postgres=# \dt+ data
List of relations
┌────────┬──────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
└────────┴──────┴───────┴───────┴─────────┴─────────────┘
(1 row)

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

should be.

Some is missing still - there is not any total size across all partitions.

maybe new command like

\dtP+ .. show partitioned tables and their size

Regards

Pavel

Show quoted text

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

#4Jeevan Ladhe
jeevan.ladhe@enterprisedb.com
In reply to: Pavel Stehule (#3)
Re: ToDo: show size of partitioned table

On Fri, Jun 1, 2018 at 8:51 PM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

2018-06-01 17:15 GMT+02:00 Ashutosh Bapat <ashutosh.bapat@enterprisedb.com

:

On Fri, Jun 1, 2018 at 12:56 AM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Hi

postgres=# SELECT count(*) from data;
┌─────────┐
│ count │
╞═════════╡
│ 1000000 │
└─────────┘
(1 row)

\dt+ can display actual size of partitioned table data - now zero is
displayed

postgres=# \dt+ data
List of relations
┌────────┬──────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
└────────┴──────┴───────┴───────┴─────────┴─────────────┘
(1 row)

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

should be.

Yes, or maybe we can add that info in "Description".

Some is missing still - there is not any total size across all partitions.

maybe new command like

\dtP+ .. show partitioned tables and their size

+1

Regards,
Jeevan Ladhe

#5Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Ashutosh Bapat (#2)
Re: ToDo: show size of partitioned table

On 2018/06/02 0:15, Ashutosh Bapat wrote:

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

There was a long discussion last year (during PG 10 beta period), such as
[1]: /messages/by-id/7dfc13c5-d6b7-1ff1-4bef-d75d6d2f76d9@lab.ntt.co.jp
finally do it for PG 12, if not PG 11.

Regarding showing the size of partitioned tables, there are many opinions
and it's not clear if showing it in \dt itself is appropriate. For one,
there is no pg_relation_size() or pg_table_size() equivalent in the
backend for aggregating the size of all tables in a partition tree and I
think people are not quite on board about having such a function in the
backend [2]/messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp.

Thanks,
Amit

[1]: /messages/by-id/7dfc13c5-d6b7-1ff1-4bef-d75d6d2f76d9@lab.ntt.co.jp
/messages/by-id/7dfc13c5-d6b7-1ff1-4bef-d75d6d2f76d9@lab.ntt.co.jp

[2]: /messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp
/messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp

#6Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#5)
Re: ToDo: show size of partitioned table

2018-06-20 7:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

On 2018/06/02 0:15, Ashutosh Bapat wrote:

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

There was a long discussion last year (during PG 10 beta period), such as
[1], and it seems most of us agreed to doing the above. Maybe, we should
finally do it for PG 12, if not PG 11.

Regarding showing the size of partitioned tables, there are many opinions
and it's not clear if showing it in \dt itself is appropriate. For one,
there is no pg_relation_size() or pg_table_size() equivalent in the
backend for aggregating the size of all tables in a partition tree and I
think people are not quite on board about having such a function in the
backend [2].

Now, the number of partitions can be low, but if the Postgres can better
process high number of partitions, then for some tables we can have
hundreds partitions.

Then usual \dt can be not too much usable. The aggregation can be done on
client side. But maybe this idea is premature. Now, for PG 12, we can start
with

\dtP+ command for showing partition tables only with aggregate size via all
related partitions.

Is it acceptable idea?

Regards

Pavel

Show quoted text

Thanks,
Amit

[1]
/messages/by-id/7dfc13c5-d6b7-
1ff1-4bef-d75d6d2f76d9%40lab.ntt.co.jp

[2]
/messages/by-id/495cec7e-f8d9-
7e13-4807-90dbf4eec4ea%40lab.ntt.co.jp

#7Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#6)
Re: ToDo: show size of partitioned table

On 2018/06/20 16:21, Pavel Stehule wrote:

2018-06-20 7:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

On 2018/06/02 0:15, Ashutosh Bapat wrote:

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

There was a long discussion last year (during PG 10 beta period), such as
[1], and it seems most of us agreed to doing the above. Maybe, we should
finally do it for PG 12, if not PG 11.

Regarding showing the size of partitioned tables, there are many opinions
and it's not clear if showing it in \dt itself is appropriate. For one,
there is no pg_relation_size() or pg_table_size() equivalent in the
backend for aggregating the size of all tables in a partition tree and I
think people are not quite on board about having such a function in the
backend [2].

Now, the number of partitions can be low, but if the Postgres can better
process high number of partitions, then for some tables we can have
hundreds partitions.

Then usual \dt can be not too much usable. The aggregation can be done on
client side. But maybe this idea is premature. Now, for PG 12, we can start
with

\dtP+ command for showing partition tables only with aggregate size via all
related partitions.

Is it acceptable idea?

Do you mean \dt continues to show size 0 for partitioned tables, but with
the new option (\dtP+) shows the actual size by aggregating across
partitions? +1 to such a feature, but we need to agree on an acceptable
implementation for that. How does the aggregation happen:

1. In a new dedicated function in the backend (parallel to pg_table_size)?

or

2. psql issues a separate query to compute the total size of a partition
tree

For option 2, I had posted a patch that simplifies writing such a query
and posted that here:

/messages/by-id/7a9c5328-5328-52a3-2a3d-bf1434b4dd1d@lab.ntt.co.jp

With that patch, the query to get the total size of a partition tree
becomes as simple as:

select sum(pg_table_size(p)) as size
from pg_get_inheritance_tables('partitioned_table_name') p

Thanks,
Amit

#8Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#7)
Re: ToDo: show size of partitioned table

2018-06-20 9:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

On 2018/06/20 16:21, Pavel Stehule wrote:

2018-06-20 7:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

On 2018/06/02 0:15, Ashutosh Bapat wrote:

I think we should at least display "Type" as "partitioned table" for a
partitioned table, so that it's easy to understand why the size is 0;
partitioned tables do not hold any data by themselves.

There was a long discussion last year (during PG 10 beta period), such

as

[1], and it seems most of us agreed to doing the above. Maybe, we

should

finally do it for PG 12, if not PG 11.

Regarding showing the size of partitioned tables, there are many

opinions

and it's not clear if showing it in \dt itself is appropriate. For one,
there is no pg_relation_size() or pg_table_size() equivalent in the
backend for aggregating the size of all tables in a partition tree and I
think people are not quite on board about having such a function in the
backend [2].

Now, the number of partitions can be low, but if the Postgres can better
process high number of partitions, then for some tables we can have
hundreds partitions.

Then usual \dt can be not too much usable. The aggregation can be done on
client side. But maybe this idea is premature. Now, for PG 12, we can

start

with

\dtP+ command for showing partition tables only with aggregate size via

all

related partitions.

Is it acceptable idea?

Do you mean \dt continues to show size 0 for partitioned tables, but with
the new option (\dtP+) shows the actual size by aggregating across
partitions? +1 to such a feature, but we need to agree on an acceptable
implementation for that. How does the aggregation happen:

yes - my proposal is no change for \dt for now. I think so we will have to
change it, when partitioning will be more common and number of partitions
will be high. But it is not today.

\dtP shows only partitions tables (like \dtS shows only system tables),
with "+" shows sum of all related partitions.

1. In a new dedicated function in the backend (parallel to pg_table_size)?

or

2. psql issues a separate query to compute the total size of a partition
tree

In this moment we can simply do sum on client side, so it is related to @2.

For option 2, I had posted a patch that simplifies writing such a query
and posted that here:

/messages/by-id/7a9c5328-5328-52a3-
2a3d-bf1434b4dd1d%40lab.ntt.co.jp

With that patch, the query to get the total size of a partition tree
becomes as simple as:

select sum(pg_table_size(p)) as size
from pg_get_inheritance_tables('partitioned_table_name') p

good to know it. Thank you. Do you think so your patch should be included
to this feature or will be processed independently?

Regards

Pavel

Show quoted text

Thanks,
Amit

#9Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#8)
Re: ToDo: show size of partitioned table

On 2018/06/20 16:50, Pavel Stehule wrote:

2018-06-20 9:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

Do you mean \dt continues to show size 0 for partitioned tables, but with
the new option (\dtP+) shows the actual size by aggregating across
partitions? +1 to such a feature, but we need to agree on an acceptable
implementation for that. How does the aggregation happen:

yes - my proposal is no change for \dt for now. I think so we will have to
change it, when partitioning will be more common and number of partitions
will be high. But it is not today.

\dtP shows only partitions tables (like \dtS shows only system tables),
with "+" shows sum of all related partitions.

Ah, okay. That makes sense.

1. In a new dedicated function in the backend (parallel to pg_table_size)?

or

2. psql issues a separate query to compute the total size of a partition
tree

In this moment we can simply do sum on client side, so it is related to @2.

I see, okay.

For option 2, I had posted a patch that simplifies writing such a query
and posted that here:

/messages/by-id/7a9c5328-5328-52a3-
2a3d-bf1434b4dd1d%40lab.ntt.co.jp

With that patch, the query to get the total size of a partition tree
becomes as simple as:

select sum(pg_table_size(p)) as size
from pg_get_inheritance_tables('partitioned_table_name') p

good to know it. Thank you. Do you think so your patch should be included
to this feature or will be processed independently?

It seems that it would be useful on its own, as people may want to do
various things once we provide them pg_get_inheritance_table.

Thanks,
Amit

#10Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#9)
Re: ToDo: show size of partitioned table

2018-06-20 10:03 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

On 2018/06/20 16:50, Pavel Stehule wrote:

2018-06-20 9:44 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

Do you mean \dt continues to show size 0 for partitioned tables, but

with

the new option (\dtP+) shows the actual size by aggregating across
partitions? +1 to such a feature, but we need to agree on an acceptable
implementation for that. How does the aggregation happen:

yes - my proposal is no change for \dt for now. I think so we will have

to

change it, when partitioning will be more common and number of partitions
will be high. But it is not today.

\dtP shows only partitions tables (like \dtS shows only system tables),
with "+" shows sum of all related partitions.

Ah, okay. That makes sense.

1. In a new dedicated function in the backend (parallel to

pg_table_size)?

or

2. psql issues a separate query to compute the total size of a partition
tree

In this moment we can simply do sum on client side, so it is related to

@2.

I see, okay.

For option 2, I had posted a patch that simplifies writing such a query
and posted that here:

/messages/by-id/7a9c5328-5328-52a3-
2a3d-bf1434b4dd1d%40lab.ntt.co.jp

With that patch, the query to get the total size of a partition tree
becomes as simple as:

select sum(pg_table_size(p)) as size
from pg_get_inheritance_tables('partitioned_table_name') p

good to know it. Thank you. Do you think so your patch should be included
to this feature or will be processed independently?

It seems that it would be useful on its own, as people may want to do
various things once we provide them pg_get_inheritance_table.

ok

I'll prepare patch and I'll do note about dependency on your patch.

Regards

Pavel

Show quoted text

Thanks,
Amit

#11Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#10)
1 attachment(s)
Re: ToDo: show size of partitioned table

Hi

I am sending a prototype of patch. Now, it calculates size of partitioned
tables with recursive query. When any more simple method will be possible,
the size calculation will be changed.

postgres=# \dt+
                       List of relations
+--------+------------+-------+-------+---------+-------------+
| Schema |    Name    | Type  | Owner |  Size   | Description |
+--------+------------+-------+-------+---------+-------------+
| public | data       | table | pavel | 0 bytes |             |
| public | data_2016  | table | pavel | 15 MB   |             |
| public | data_2017  | table | pavel | 15 MB   |             |
| public | data_other | table | pavel | 11 MB   |             |
+--------+------------+-------+-------+---------+-------------+
(4 rows)
postgres=# \dP+
          List of partitioned tables
+--------+------+-------+-------+-------------+
| Schema | Name | Owner | Size  | Description |
+--------+------+-------+-------+-------------+
| public | data | pavel | 42 MB |             |
+--------+------+-------+-------+-------------+
(1 row)

Regards

Pavel

p.s. Another patch can be replacement of relation type from "table" to
"partitioned table"

postgres=# \dt+
                             List of relations
+--------+------------+-------------------+-------+---------+-------------+
| Schema |    Name    |       Type        | Owner |  Size   | Description |
+--------+------------+-------------------+-------+---------+-------------+
| public | data       | partitioned table | pavel | 0 bytes |             |
| public | data_2016  | table             | pavel | 15 MB   |             |
| public | data_2017  | table             | pavel | 15 MB   |             |
| public | data_other | table             | pavel | 11 MB   |             |
+--------+------------+-------------------+-------+---------+-------------+
(4 rows)

A patch is simple for this case

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c3bdf8555d..491e58eb29 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3490,8 +3490,8 @@ listTables(const char *tabtypes, const char *pattern,
bool verbose, bool showSys
                      gettext_noop("sequence"),
                      gettext_noop("special"),
                      gettext_noop("foreign table"),
-                     gettext_noop("table"),    /* partitioned table */
-                     gettext_noop("index"),    /* partitioned index */
+                     gettext_noop("partitioned table"),    /* partitioned
table */
+                     gettext_noop("partitioned index"),    /* partitioned
index */
                      gettext_noop("Type"),
                      gettext_noop("Owner"));

Attachments:

psql-dP-initial.patchtext/x-patch; charset=US-ASCII; name=psql-dP-initial.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b17039d60f..d2eb701a96 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,21 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 4c85f43f09..09f49b4f39 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -785,6 +785,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				success = listPartitions(pattern, show_verbose);
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c3bdf8555d..4542fd511c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3601,6 +3601,122 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP
+ */
+bool
+listPartitions(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+
+	initPQExpBuffer(&buf);
+
+	/*
+	 * Note: as of Pg 8.2, we no longer use relkind 's' (special), but we keep
+	 * it here for backwards compatibility.
+	 */
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (verbose)
+	{
+		/* CTE is supported from 8.4 */
+		if (pset.sversion >= 80400)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n");
+
+			if (pset.sversion >= 90000)
+				appendPQExpBuffer(&buf,
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_table_size(");
+			else
+				appendPQExpBuffer(&buf,
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_relation_size(");
+
+			appendPQExpBuffer(&buf,
+							  "oid))) FROM d) AS \"%s\"",
+							  gettext_noop("Size"));
+		}
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			psql_error("Did not find any relation named \"%s\".\n",
+					   pattern);
+		else
+			psql_error("Did not find any relations.\n");
+	}
+	else
+	{
+		myopt.nullPrint = NULL;
+		myopt.title = _("List of partitioned tables");
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..0fb7c4b91e 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP */
+extern bool listPartitions(const char *pattern, bool verbose);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 702e742af4..ccb85e8cb1 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -254,6 +254,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
+	fprintf(output, _("  \\dP[+] [PATTERN]       list partitioned tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8ee9..ce911983dc 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1621,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
#12Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#11)
Re: ToDo: show size of partitioned table

Hi Pavel.

On 2018/07/23 20:46, Pavel Stehule wrote:

Hi

I am sending a prototype of patch. Now, it calculates size of partitioned
tables with recursive query. When any more simple method will be possible,
the size calculation will be changed.

postgres=# \dt+
List of relations
+--------+------------+-------+-------+---------+-------------+
| Schema |    Name    | Type  | Owner |  Size   | Description |
+--------+------------+-------+-------+---------+-------------+
| public | data       | table | pavel | 0 bytes |             |
| public | data_2016  | table | pavel | 15 MB   |             |
| public | data_2017  | table | pavel | 15 MB   |             |
| public | data_other | table | pavel | 11 MB   |             |
+--------+------------+-------+-------+---------+-------------+
(4 rows)
postgres=# \dP+
List of partitioned tables
+--------+------+-------+-------+-------------+
| Schema | Name | Owner | Size  | Description |
+--------+------+-------+-------+-------------+
| public | data | pavel | 42 MB |             |
+--------+------+-------+-------+-------------+
(1 row)

This looks nice, although I haven't looked at the patch yet. Also, as you
said, we could later replace the method of directly querying pg_inherits
by something else.

p.s. Another patch can be replacement of relation type from "table" to
"partitioned table"

postgres=# \dt+
List of relations
+--------+------------+-------------------+-------+---------+-------------+
| Schema |    Name    |       Type        | Owner |  Size   | Description |
+--------+------------+-------------------+-------+---------+-------------+
| public | data       | partitioned table | pavel | 0 bytes |             |
| public | data_2016  | table             | pavel | 15 MB   |             |
| public | data_2017  | table             | pavel | 15 MB   |             |
| public | data_other | table             | pavel | 11 MB   |             |
+--------+------------+-------------------+-------+---------+-------------+
(4 rows)

A patch is simple for this case

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c3bdf8555d..491e58eb29 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3490,8 +3490,8 @@ listTables(const char *tabtypes, const char *pattern,
bool verbose, bool showSys
gettext_noop("sequence"),
gettext_noop("special"),
gettext_noop("foreign table"),
-                     gettext_noop("table"),    /* partitioned table */
-                     gettext_noop("index"),    /* partitioned index */
+                     gettext_noop("partitioned table"),    /* partitioned
table */
+                     gettext_noop("partitioned index"),    /* partitioned
index */
gettext_noop("Type"),
gettext_noop("Owner"));

Inclined to +1 this, too. Although, one might ask why partitioned tables
are called so only in the psql's output, but not in the backend's error
messages, for example, as was discussed in the past.

Thanks,
Amit

#13Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#12)
Re: ToDo: show size of partitioned table

2018-07-25 11:09 GMT+02:00 Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>:

Hi Pavel.

On 2018/07/23 20:46, Pavel Stehule wrote:

Hi

I am sending a prototype of patch. Now, it calculates size of partitioned
tables with recursive query. When any more simple method will be

possible,

the size calculation will be changed.

postgres=# \dt+
List of relations
+--------+------------+-------+-------+---------+-------------+
| Schema |    Name    | Type  | Owner |  Size   | Description |
+--------+------------+-------+-------+---------+-------------+
| public | data       | table | pavel | 0 bytes |             |
| public | data_2016  | table | pavel | 15 MB   |             |
| public | data_2017  | table | pavel | 15 MB   |             |
| public | data_other | table | pavel | 11 MB   |             |
+--------+------------+-------+-------+---------+-------------+
(4 rows)
postgres=# \dP+
List of partitioned tables
+--------+------+-------+-------+-------------+
| Schema | Name | Owner | Size  | Description |
+--------+------+-------+-------+-------------+
| public | data | pavel | 42 MB |             |
+--------+------+-------+-------+-------------+
(1 row)

This looks nice, although I haven't looked at the patch yet. Also, as you
said, we could later replace the method of directly querying pg_inherits
by something else.

p.s. Another patch can be replacement of relation type from "table" to
"partitioned table"

postgres=# \dt+
List of relations
+--------+------------+-------------------+-------+---------

+-------------+

| Schema | Name | Type | Owner | Size |

Description |

+--------+------------+-------------------+-------+---------

+-------------+

| public | data | partitioned table | pavel | 0 bytes |

|

| public | data_2016 | table | pavel | 15 MB |

|

| public | data_2017 | table | pavel | 15 MB |

|

| public | data_other | table | pavel | 11 MB |

|

+--------+------------+-------------------+-------+---------

+-------------+

(4 rows)

A patch is simple for this case

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c3bdf8555d..491e58eb29 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3490,8 +3490,8 @@ listTables(const char *tabtypes, const char

*pattern,

bool verbose, bool showSys
gettext_noop("sequence"),
gettext_noop("special"),
gettext_noop("foreign table"),
-                     gettext_noop("table"),    /* partitioned table */
-                     gettext_noop("index"),    /* partitioned index */
+                     gettext_noop("partitioned table"),    /*

partitioned

table */
+ gettext_noop("partitioned index"), /*

partitioned

index */
gettext_noop("Type"),
gettext_noop("Owner"));

Inclined to +1 this, too. Although, one might ask why partitioned tables
are called so only in the psql's output, but not in the backend's error
messages, for example, as was discussed in the past.

i think so error messages is different chapter - it means revision of all
error messages, and probably somewhere the name of partition, and not
partition table can be correct.

Personally I am not sure about benefit to change error messages. Now, the
partition is table too, so the error messages are not strongly wrong.

Regards

Pavel

Show quoted text

Thanks,
Amit

#14Mathias Brossard
postgresql@zoinx.org
In reply to: Pavel Stehule (#13)
Re: ToDo: show size of partitioned table

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: tested, passed

Hi,

I'm with Melanie Plageman running the SVPUG Patch Review Meetup. I tested this feature on my Mac. The patch applied cleanly on master, and the feature worked as expected with the SQL at the bottom of this email (Jesse Zhang suggested the two-level partitioning). installcheck passed but installcheck-world did not.

I do have a feedback on the implementation. The code tries to support older PostgreSQL server versions when declarative partitions were not supported before version 10 (relkind value of 'p'). Those versions will never return any result from the query being built. So I would suggest an early return from the function. The upside would be that the query building would be simpler. I can make patch implementing that suggestion if you want.

Sincerely,
-- Mathias Brossard

CREATE TABLE partition (
part int not null,
value int not null
) PARTITION BY RANGE (part);

CREATE TABLE partition_0 PARTITION OF partition FOR VALUES FROM (0) TO (10);
CREATE TABLE partition_1 PARTITION OF partition FOR VALUES FROM (10) TO (20);
CREATE TABLE partition_2 PARTITION OF partition FOR VALUES FROM (20) TO (30);
CREATE TABLE partition_3 PARTITION OF partition FOR VALUES FROM (30) TO (40);
CREATE TABLE partition_4 PARTITION OF partition FOR VALUES FROM (40) TO (50);
CREATE TABLE partition_5 PARTITION OF partition FOR VALUES FROM (50) TO (60);
CREATE TABLE partition_6 PARTITION OF partition FOR VALUES FROM (60) TO (70);
CREATE TABLE partition_7 PARTITION OF partition FOR VALUES FROM (70) TO (80);
CREATE TABLE partition_8 PARTITION OF partition FOR VALUES FROM (80) TO (90);
CREATE TABLE partition_9 (
part int not null,
value int not null
) PARTITION BY RANGE (part);

CREATE TABLE partition_9a PARTITION OF partition_9 FOR VALUES FROM (90) TO (95);
CREATE TABLE partition_9b PARTITION OF partition_9 FOR VALUES FROM (95) TO (100);
ALTER TABLE partition ATTACH PARTITION partition_9 FOR VALUES FROM (90) TO (100);

INSERT INTO partition SELECT i % 100 AS part, i AS value FROM generate_series(1, 1000000) AS i;

-------------------
-- Below is the resulting output

test=# \dt+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------------+-------+-----------+---------+-------------
public | partition | table | mbrossard | 0 bytes |
public | partition_0 | table | mbrossard | 3568 kB |
public | partition_1 | table | mbrossard | 3568 kB |
public | partition_2 | table | mbrossard | 3568 kB |
public | partition_3 | table | mbrossard | 3568 kB |
public | partition_4 | table | mbrossard | 3568 kB |
public | partition_5 | table | mbrossard | 3568 kB |
public | partition_6 | table | mbrossard | 3568 kB |
public | partition_7 | table | mbrossard | 3568 kB |
public | partition_8 | table | mbrossard | 3568 kB |
public | partition_9 | table | mbrossard | 0 bytes |
public | partition_9a | table | mbrossard | 1800 kB |
public | partition_9b | table | mbrossard | 1800 kB |
(13 rows)

test=# \dP+
List of partitioned tables
Schema | Name | Owner | Size | Description
--------+-------------+-----------+---------+-------------
public | partition | mbrossard | 35 MB |
public | partition_9 | mbrossard | 3600 kB |
(2 rows)

test=# \dP+ *9
List of partitioned tables
Schema | Name | Owner | Size | Description
--------+-------------+-----------+---------+-------------
public | partition_9 | mbrossard | 3600 kB |
(1 row)

#15Pavel Stehule
pavel.stehule@gmail.com
In reply to: Mathias Brossard (#14)
Re: ToDo: show size of partitioned table

Hi

čt 16. 8. 2018 v 5:52 odesílatel Mathias Brossard <postgresql@zoinx.org>
napsal:

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: tested, passed

Hi,

I'm with Melanie Plageman running the SVPUG Patch Review Meetup. I tested
this feature on my Mac. The patch applied cleanly on master, and the
feature worked as expected with the SQL at the bottom of this email (Jesse
Zhang suggested the two-level partitioning). installcheck passed but
installcheck-world did not.

I do have a feedback on the implementation. The code tries to support
older PostgreSQL server versions when declarative partitions were not
supported before version 10 (relkind value of 'p'). Those versions will
never return any result from the query being built. So I would suggest an
early return from the function. The upside would be that the query building
would be simpler. I can make patch implementing that suggestion if you want.

This is question - maybe we can support older partitioning based on only
inheritance - and the query can be more exact on PostgreSQL 10 and newer.

Please, send any patch. You are welcome.

Regards

Pavel

Show quoted text

Sincerely,
-- Mathias Brossard

CREATE TABLE partition (
part int not null,
value int not null
) PARTITION BY RANGE (part);

CREATE TABLE partition_0 PARTITION OF partition FOR VALUES FROM (0) TO
(10);
CREATE TABLE partition_1 PARTITION OF partition FOR VALUES FROM (10) TO
(20);
CREATE TABLE partition_2 PARTITION OF partition FOR VALUES FROM (20) TO
(30);
CREATE TABLE partition_3 PARTITION OF partition FOR VALUES FROM (30) TO
(40);
CREATE TABLE partition_4 PARTITION OF partition FOR VALUES FROM (40) TO
(50);
CREATE TABLE partition_5 PARTITION OF partition FOR VALUES FROM (50) TO
(60);
CREATE TABLE partition_6 PARTITION OF partition FOR VALUES FROM (60) TO
(70);
CREATE TABLE partition_7 PARTITION OF partition FOR VALUES FROM (70) TO
(80);
CREATE TABLE partition_8 PARTITION OF partition FOR VALUES FROM (80) TO
(90);
CREATE TABLE partition_9 (
part int not null,
value int not null
) PARTITION BY RANGE (part);

CREATE TABLE partition_9a PARTITION OF partition_9 FOR VALUES FROM (90) TO
(95);
CREATE TABLE partition_9b PARTITION OF partition_9 FOR VALUES FROM (95) TO
(100);
ALTER TABLE partition ATTACH PARTITION partition_9 FOR VALUES FROM (90) TO
(100);

INSERT INTO partition SELECT i % 100 AS part, i AS value FROM
generate_series(1, 1000000) AS i;

-------------------
-- Below is the resulting output

test=# \dt+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------------+-------+-----------+---------+-------------
public | partition | table | mbrossard | 0 bytes |
public | partition_0 | table | mbrossard | 3568 kB |
public | partition_1 | table | mbrossard | 3568 kB |
public | partition_2 | table | mbrossard | 3568 kB |
public | partition_3 | table | mbrossard | 3568 kB |
public | partition_4 | table | mbrossard | 3568 kB |
public | partition_5 | table | mbrossard | 3568 kB |
public | partition_6 | table | mbrossard | 3568 kB |
public | partition_7 | table | mbrossard | 3568 kB |
public | partition_8 | table | mbrossard | 3568 kB |
public | partition_9 | table | mbrossard | 0 bytes |
public | partition_9a | table | mbrossard | 1800 kB |
public | partition_9b | table | mbrossard | 1800 kB |
(13 rows)

test=# \dP+
List of partitioned tables
Schema | Name | Owner | Size | Description
--------+-------------+-----------+---------+-------------
public | partition | mbrossard | 35 MB |
public | partition_9 | mbrossard | 3600 kB |
(2 rows)

test=# \dP+ *9
List of partitioned tables
Schema | Name | Owner | Size | Description
--------+-------------+-----------+---------+-------------
public | partition_9 | mbrossard | 3600 kB |
(1 row)

#16Mathias Brossard
mathias@brossard.org
In reply to: Pavel Stehule (#15)
1 attachment(s)
Re: ToDo: show size of partitioned table

On Thu, Aug 16, 2018 at 12:46 AM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

čt 16. 8. 2018 v 5:52 odesílatel Mathias Brossard <postgresql@zoinx.org>
napsal:

I do have a feedback on the implementation. The code tries to support
older PostgreSQL server versions when declarative partitions were not
supported before version 10 (relkind value of 'p'). Those versions will
never return any result from the query being built. So I would suggest an
early return from the function. The upside would be that the query building
would be simpler. I can make patch implementing that suggestion if you want.

This is question - maybe we can support older partitioning based on only
inheritance - and the query can be more exact on PostgreSQL 10 and newer.

Please, send any patch. You are welcome.

In my very humble opinion, I would restrict the definition of partitions to
declarative partitioning. My justification would be that partitions all use
inheritance, but not all inheritance is a partition (how would you handle
multiple inheritance).

See patch attached that fails (in a way similar to other features) when
connected to servers with version earlier than 10.0.

Sincerely,
-- Mathias Brossard

Attachments:

psql-dP-10+only.patchapplication/octet-stream; name=psql-dP-10+only.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eb9d93a..89c60aa 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,21 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a..9d76bec 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -766,6 +766,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				success = listPartitions(pattern, show_verbose);
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 80d8338..87957ea 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3634,6 +3634,118 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP
+ */
+bool
+listPartitions(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\n  (WITH RECURSIVE d\n"
+						  "        AS (SELECT inhrelid AS oid\n"
+						  "              FROM pg_inherits\n"
+						  "             WHERE inhparent = c.oid\n"
+						  "            UNION ALL\n"
+						  "            SELECT inhrelid\n"
+						  "              FROM pg_inherits i\n"
+						  "                   JOIN d ON i.inhparent = d.oid)\n"
+						  "         SELECT pg_catalog.pg_size_pretty(sum(pg_table_size("
+						  "oid))) FROM d) AS \"%s\""
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Size"),
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			psql_error("Did not find any relation named \"%s\".\n",
+					   pattern);
+		else
+			psql_error("Did not find any relations.\n");
+	}
+	else
+	{
+		myopt.nullPrint = NULL;
+		myopt.title = _("List of partitioned tables");
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5ef..0fb7c4b 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP */
+extern bool listPartitions(const char *pattern, bool verbose);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 316030d..aa4c20e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -254,6 +254,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
+	fprintf(output, _("  \\dP[+] [PATTERN]       list partitioned tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bb696f8..ce91198 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1621,7 +1621,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
#17Michael Paquier
michael@paquier.xyz
In reply to: Amit Langote (#12)
Re: ToDo: show size of partitioned table

On Wed, Jul 25, 2018 at 06:09:05PM +0900, Amit Langote wrote:

This looks nice, although I haven't looked at the patch yet. Also, as you
said, we could later replace the method of directly querying pg_inherits
by something else.

Yes, at the end we may be looking at something like that which would
avoid the need of any WITH RECURSIVE queries:
https://commitfest.postgresql.org/19/1694/

Tha patch still applies, but I think that we'd want to do something for
the partition tree function first, and then come to this one. Moved to
next CF.
--
Michael

#18Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Mathias Brossard (#16)
Re: ToDo: show size of partitioned table

Hi Mathias, Pavel,

On 2018/08/17 12:26, Mathias Brossard wrote:

On Thu, Aug 16, 2018 at 12:46 AM Pavel Stehule <pavel.stehule@gmail.com>

This is question - maybe we can support older partitioning based on only
inheritance - and the query can be more exact on PostgreSQL 10 and newer.

Please, send any patch. You are welcome.

In my very humble opinion, I would restrict the definition of partitions to
declarative partitioning. My justification would be that partitions all use
inheritance, but not all inheritance is a partition (how would you handle
multiple inheritance).

See patch attached that fails (in a way similar to other features) when
connected to servers with version earlier than 10.0.

The patch to add the pg_partition_tree() function was just committed:

Add pg_partition_tree to display information about partitions
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=d5eec4eefde70

Could one of you please revise the patch to use that function to produce
the output of \dP+?

Note that pg_partition_tree simply scans the underlying catalog to get all
the tables, so if you pass it a table that's not partitioned (relkind ==
'r'), but has inheritance children, the children will be returned in its
output. So, if you want to limit the output of \dP to partitioned tables,
be sure to include relkind = 'p' condition in the query.

Thanks,
Amit

#19Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#18)
1 attachment(s)
Re: ToDo: show size of partitioned table

Hi

út 30. 10. 2018 v 7:52 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

Hi Mathias, Pavel,

On 2018/08/17 12:26, Mathias Brossard wrote:

On Thu, Aug 16, 2018 at 12:46 AM Pavel Stehule <pavel.stehule@gmail.com>

This is question - maybe we can support older partitioning based on only
inheritance - and the query can be more exact on PostgreSQL 10 and

newer.

Please, send any patch. You are welcome.

In my very humble opinion, I would restrict the definition of partitions

to

declarative partitioning. My justification would be that partitions all

use

inheritance, but not all inheritance is a partition (how would you handle
multiple inheritance).

See patch attached that fails (in a way similar to other features) when
connected to servers with version earlier than 10.0.

The patch to add the pg_partition_tree() function was just committed:

Add pg_partition_tree to display information about partitions

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=d5eec4eefde70

Could one of you please revise the patch to use that function to produce
the output of \dP+?

here it is.

It is based on Mathias's patch. Although we can use pg_partition_tree on
PostgreSQL, we still should to support PostgreSQL 10, 11 where this
function is not available

Regards

Pavel

Show quoted text

Note that pg_partition_tree simply scans the underlying catalog to get all
the tables, so if you pass it a table that's not partitioned (relkind ==
'r'), but has inheritance children, the children will be returned in its
output. So, if you want to limit the output of \dP to partitioned tables,
be sure to include relkind = 'p' condition in the query.

Thanks,
Amit

Attachments:

psql-dP-2.patchtext/x-patch; charset=US-ASCII; name=psql-dP-2.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eb9d93a168..89c60aa340 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,21 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..9d76bec38e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -766,6 +766,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				success = listPartitions(pattern, show_verbose);
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..9c69351277 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3635,6 +3635,132 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP
+ */
+bool
+listPartitions(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 11 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
+							  "relid)))\n"
+							  "     FROM pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			psql_error("Did not find any relation named \"%s\".\n",
+					   pattern);
+		else
+			psql_error("Did not find any relations.\n");
+	}
+	else
+	{
+		myopt.nullPrint = NULL;
+		myopt.title = _("List of partitioned tables");
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..0fb7c4b91e 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP */
+extern bool listPartitions(const char *pattern, bool verbose);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..07d46ef3cf 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -254,6 +254,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
+	fprintf(output, _("  \\dP[+] [PATTERN]       list partitioned tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a980f92e11..7b57a79e38 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1332,7 +1332,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
#20Amit Langote
amitlangote09@gmail.com
In reply to: Pavel Stehule (#19)
Re: ToDo: show size of partitioned table

On Tue, Oct 30, 2018 at 8:04 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:

út 30. 10. 2018 v 7:52 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> napsal:

The patch to add the pg_partition_tree() function was just committed:

Add pg_partition_tree to display information about partitions
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=d5eec4eefde70

Could one of you please revise the patch to use that function to produce
the output of \dP+?

here it is.

It is based on Mathias's patch. Although we can use pg_partition_tree on PostgreSQL, we still should to support PostgreSQL 10, 11 where this function is not available

Ah, I forgot that psql will need to consider 10 and 11 servers too.

Thanks,
Amit

#21Michael Paquier
michael@paquier.xyz
In reply to: Amit Langote (#20)
Re: ToDo: show size of partitioned table

On Tue, Oct 30, 2018 at 09:24:01PM +0900, Amit Langote wrote:

It is based on Mathias's patch. Although we can use
pg_partition_tree on PostgreSQL, we still should to support
PostgreSQL 10, 11 where this function is not available

Ah, I forgot that psql will need to consider 10 and 11 servers too.

+ /* PostgreSQL 11 has pg_partition_tree function */
That's v12 here.

+    appendPQExpBuffer(&buf,
+    ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
+    "relid)))\n"
+    "     FROM pg_partition_tree(c.oid)) AS \"%s\""
You need to do schema qualification for functions and relations.
--
Michael
#22Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#19)
Re: ToDo: show size of partitioned table

On 2018/10/30 20:03, Pavel Stehule wrote:

út 30. 10. 2018 v 7:52 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

Could one of you please revise the patch to use that function to produce
the output of \dP+?

here it is.

It is based on Mathias's patch. Although we can use pg_partition_tree on
PostgreSQL, we still should to support PostgreSQL 10, 11 where this
function is not available

Thanks for updating the patch. Just a couple of comments:

+ is used, a sum of size of related partitions and a description

I suggest:

is used, the sum of sizes of related partitions and associated description

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they should
have a separate command.

+    if (PQntuples(res) == 0 && !pset.quiet)
+    {
+        if (pattern)
+            psql_error("Did not find any relation named \"%s\".\n",
+                       pattern);
+        else
+            psql_error("Did not find any relations.\n");
+    }

I think we should use "partitioned table" instead of "relation" in the
above error messages, because this command is specifically finding
partitioned tables.

(If we decide to include partitioned indexes as well, then the above error
message should say "partitioned relation")

+ fprintf(output, _(" \\dP[+] [PATTERN] list partitioned
tables\n"));

Again, if we include indexes, this should be "partitioned relations".

How about adding a couple of regression tests?

Thanks,
Amit

#23Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#22)
Re: ToDo: show size of partitioned table

st 31. 10. 2018 v 3:27 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

On 2018/10/30 20:03, Pavel Stehule wrote:

út 30. 10. 2018 v 7:52 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

Could one of you please revise the patch to use that function to produce
the output of \dP+?

here it is.

It is based on Mathias's patch. Although we can use pg_partition_tree on
PostgreSQL, we still should to support PostgreSQL 10, 11 where this
function is not available

Thanks for updating the patch. Just a couple of comments:

+ is used, a sum of size of related partitions and a description

I suggest:

is used, the sum of sizes of related partitions and associated description

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they should
have a separate command.

I though about it too and I prefer separate command. Similar to \di+

+    if (PQntuples(res) == 0 && !pset.quiet)
+    {
+        if (pattern)
+            psql_error("Did not find any relation named \"%s\".\n",
+                       pattern);
+        else
+            psql_error("Did not find any relations.\n");
+    }

I think we should use "partitioned table" instead of "relation" in the
above error messages, because this command is specifically finding
partitioned tables.

(If we decide to include partitioned indexes as well, then the above error
message should say "partitioned relation")

+ fprintf(output, _(" \\dP[+] [PATTERN] list partitioned
tables\n"));

Again, if we include indexes, this should be "partitioned relations".

How about adding a couple of regression tests?

I am not sure. Has not sense run this test over empty database, and some
bigger database can increase running.

More the size can be platform depend.

Regards

Pavel

Show quoted text

Thanks,
Amit

#24Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#23)
Re: ToDo: show size of partitioned table

On 2018/10/31 15:30, Pavel Stehule wrote:

st 31. 10. 2018 v 3:27 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they should
have a separate command.

I though about it too and I prefer separate command. Similar to \di+

Okay, maybe \dI+.

I am not sure. Has not sense run this test over empty database, and some
bigger database can increase running.

More the size can be platform depend.

Okay, sure.

Thanks,
Amit

#25Michael Paquier
michael@paquier.xyz
In reply to: Amit Langote (#24)
Re: ToDo: show size of partitioned table

On Wed, Oct 31, 2018 at 03:34:02PM +0900, Amit Langote wrote:

On 2018/10/31 15:30, Pavel Stehule wrote:

I am not sure. Has not sense run this test over empty database, and some
bigger database can increase running.

More the size can be platform depend.

Okay, sure.

Well, that would mostly depend on the relation page size which is
defined at compilation, no? The plans of some queries are unstable
depending on the page size so they would fail, but let's limit the
damage if possible.
--
Michael

#26Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#25)
Re: ToDo: show size of partitioned table

st 31. 10. 2018 v 8:38 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Wed, Oct 31, 2018 at 03:34:02PM +0900, Amit Langote wrote:

On 2018/10/31 15:30, Pavel Stehule wrote:

I am not sure. Has not sense run this test over empty database, and some
bigger database can increase running.

More the size can be platform depend.

Okay, sure.

Well, that would mostly depend on the relation page size which is
defined at compilation, no? The plans of some queries are unstable
depending on the page size so they would fail, but let's limit the
damage if possible.

I am not sure - I remember one private test that we did on our patches, and
this tests fails sometimes on 32bits. So I afraid about stability.

Regards

Pavel

--

Show quoted text

Michael

#27Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#26)
Re: ToDo: show size of partitioned table

On Wed, Oct 31, 2018 at 08:41:45AM +0100, Pavel Stehule wrote:

I am not sure - I remember one private test that we did on our patches, and
this tests fails sometimes on 32bits. So I afraid about stability.

That could be possible as well. I think that you are right to be afraid
of such things, and keeping the tests portable a maximum is always a
good thing I think.
--
Michael

#28Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#24)
Re: ToDo: show size of partitioned table

st 31. 10. 2018 v 7:34 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

On 2018/10/31 15:30, Pavel Stehule wrote:

st 31. 10. 2018 v 3:27 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they

should

have a separate command.

I though about it too and I prefer separate command. Similar to \di+

Okay, maybe \dI+.

what about combination

\dPt+ for partitined tables and size
\dPi+ for indexes on partitioned tables and size
\dP+ for total partition size (tables + indexes)

What do you think about it?

Regards

Pavel

Show quoted text

I am not sure. Has not sense run this test over empty database, and some
bigger database can increase running.

More the size can be platform depend.

Okay, sure.

Thanks,
Amit

#29Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#28)
Re: ToDo: show size of partitioned table

Hi,

On 2018/11/01 2:19, Pavel Stehule wrote:

st 31. 10. 2018 v 7:34 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

On 2018/10/31 15:30, Pavel Stehule wrote:

st 31. 10. 2018 v 3:27 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they

should

have a separate command.

I though about it too and I prefer separate command. Similar to \di+

Okay, maybe \dI+.

what about combination

\dPt+ for partitined tables and size
\dPi+ for indexes on partitioned tables and size
\dP+ for total partition size (tables + indexes)

+1

Thanks,
Amit

#30Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#29)
1 attachment(s)
Re: ToDo: show size of partitioned table

pá 2. 11. 2018 v 6:17 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Hi,

On 2018/11/01 2:19, Pavel Stehule wrote:

st 31. 10. 2018 v 7:34 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

On 2018/10/31 15:30, Pavel Stehule wrote:

st 31. 10. 2018 v 3:27 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

+ appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ('p')\n");

I wonder if we should list partitioned indexes ('I') as well, because
their size information is not available with \di+. But maybe, they

should

have a separate command.

I though about it too and I prefer separate command. Similar to \di+

Okay, maybe \dI+.

what about combination

\dPt+ for partitined tables and size
\dPi+ for indexes on partitioned tables and size
\dP+ for total partition size (tables + indexes)

+1

here is a patch

postgres=# \dt+
List of relations
┌────────┬───────────┬───────┬───────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Size │ Description │
╞════════╪═══════════╪═══════╪═══════╪═════════╪═════════════╡
│ public │ data │ table │ pavel │ 0 bytes │ │
│ public │ data_2016 │ table │ pavel │ 18 MB │ │
│ public │ data_2017 │ table │ pavel │ 17 MB │ │
└────────┴───────────┴───────┴───────┴─────────┴─────────────┘
(3 rows)

postgres=# \di+
List of relations
┌────────┬────────────────────────┬───────┬───────┬───────────┬─────────┬─────────────┐
│ Schema │ Name │ Type │ Owner │ Table │ Size │
Description │
╞════════╪════════════════════════╪═══════╪═══════╪═══════════╪═════════╪═════════════╡
│ public │ data_2016_inserted_idx │ index │ pavel │ data_2016 │ 11 MB
│ │
│ public │ data_2016_value_idx │ index │ pavel │ data_2016 │ 15 MB
│ │
│ public │ data_2017_inserted_idx │ index │ pavel │ data_2017 │ 10 MB
│ │
│ public │ data_2017_value_idx │ index │ pavel │ data_2017 │ 14 MB
│ │
│ public │ data_inserted_idx │ index │ pavel │ data │ 0 bytes
│ │
│ public │ data_value_idx │ index │ pavel │ data │ 0 bytes
│ │
└────────┴────────────────────────┴───────┴───────┴───────────┴─────────┴─────────────┘
(6 rows)

postgres=# \dP+
List of partitioned relations
┌────────┬──────┬───────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════════╡
│ public │ data │ pavel │ 84 MB │ │
└────────┴──────┴───────┴───────┴─────────────┘
(1 row)

postgres=# \dPt+
List of partitioned tables
┌────────┬──────┬───────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪══════╪═══════╪═══════╪═════════════╡
│ public │ data │ pavel │ 35 MB │ │
└────────┴──────┴───────┴───────┴─────────────┘
(1 row)

postgres=# \dPi+
List of partitioned indexes
┌────────┬───────────────────┬───────┬───────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Table │ Size │ Description │
╞════════╪═══════════════════╪═══════╪═══════╪═══════╪═════════════╡
│ public │ data_inserted_idx │ pavel │ data │ 21 MB │ │
│ public │ data_value_idx │ pavel │ data │ 28 MB │ │
└────────┴───────────────────┴───────┴───────┴───────┴─────────────┘
(2 rows)

Show quoted text

Thanks,
Amit

Attachments:

psql-dP-3.patchtext/x-patch; charset=US-ASCII; name=psql-dP-3.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eb9d93a168..fb30571e2c 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,49 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose relation name or schema name matches
+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose index name or schema name matches
+        the pattern are listed.  If the form <literal>\dPi+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\dPt+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..713638323e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -766,6 +766,16 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				if (cmd[2] == 'i')
+					success = listPartitions(pattern, show_verbose, true, false);
+				else if (cmd[2] == 't')
+					success = listPartitions(pattern, show_verbose, false, true);
+				else if (cmd[2] == '+' || cmd[2] == '\0')
+					success = listPartitions(pattern, show_verbose, false, false);
+				else
+					status = PSQL_CMD_UNKNOWN;
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..f742a37b72 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3635,6 +3635,184 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		size_function = "pg_total_relation_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("relation");
+		objects_name = gettext_noop("relations");
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (show_indexes)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("Table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 11 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)\n",
+						 relkind_str);
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c2138c26b9 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP, \dPi, \dPt */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..84c014993f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -249,6 +249,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[+]  [PATTERN]      list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[+] [PATTERN]      list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[+] [PATTERN]      list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a980f92e11..032c28ebdc 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3450,6 +3468,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
#31Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#30)
Re: ToDo: show size of partitioned table

On 2018/11/04 4:58, Pavel Stehule wrote:

here is a patch

Thank you, Pavel.

Here are some comments.

I mentioned it during the last review, but maybe you missed it due to the
other discussion.

+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        the pattern are listed.  If the form <literal>\dPi+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.
+        the pattern are listed.  If the form <literal>\dPt+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.

I suggest:

"is used, the sum of sizes of related partitions / index partitions /
table partitions and associated description are also displayed."

Note that I also fixed the typo (indexes -> tables) in the last one.

Also, I wonder if we should mention in the description of \dP+ that the
displayed size considers the sizes of both the tables and indexes on the
individual partitions, because in the code, I see pg_total_relation_size
being used. So, the text should be something like:

"is used, the sum of size of related partitions (including the table and
indexes, if any) and associated description are also displayed."

Code itself looks to me to be in good shape, except you seem to have also
missed Michael's comment upthread:

+ /* PostgreSQL 11 has pg_partition_tree function */

/* PostgreSQL 12 has pg_partition_tree function */

Thanks again.

Regards,
Amit

#32Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#31)
1 attachment(s)
Re: ToDo: show size of partitioned table

po 5. 11. 2018 v 7:20 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

On 2018/11/04 4:58, Pavel Stehule wrote:

here is a patch

Thank you, Pavel.

Here are some comments.

I mentioned it during the last review, but maybe you missed it due to the
other discussion.

+        the pattern are listed.  If the form <literal>\dP+</literal>
+        is used, a sum of size of related partitions and a description
+        are also displayed.
+        the pattern are listed.  If the form <literal>\dPi+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.
+        the pattern are listed.  If the form <literal>\dPt+</literal>
+        is used, a sum of size of related indexes and a description
+        are also displayed.

I suggest:

"is used, the sum of sizes of related partitions / index partitions /
table partitions and associated description are also displayed."

Note that I also fixed the typo (indexes -> tables) in the last one.

Also, I wonder if we should mention in the description of \dP+ that the
displayed size considers the sizes of both the tables and indexes on the
individual partitions, because in the code, I see pg_total_relation_size
being used. So, the text should be something like:

"is used, the sum of size of related partitions (including the table and
indexes, if any) and associated description are also displayed."

should be fixed now

Code itself looks to me to be in good shape, except you seem to have also
missed Michael's comment upthread:

+ /* PostgreSQL 11 has pg_partition_tree function */

/* PostgreSQL 12 has pg_partition_tree function */

should be fixed too

Thanks again.

Regards,
Amit

Updated patch attached

Regards

Pavel

Attachments:

psql-dP-4.patchtext/x-patch; charset=US-ASCII; name=psql-dP-4.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eb9d93a168..f466d5873f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,53 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5b4d54a442..713638323e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -766,6 +766,16 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				if (cmd[2] == 'i')
+					success = listPartitions(pattern, show_verbose, true, false);
+				else if (cmd[2] == 't')
+					success = listPartitions(pattern, show_verbose, false, true);
+				else if (cmd[2] == '+' || cmd[2] == '\0')
+					success = listPartitions(pattern, show_verbose, false, false);
+				else
+					status = PSQL_CMD_UNKNOWN;
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..4cab7da1ab 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3635,6 +3635,183 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		size_function = "pg_total_relation_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("relation");
+		objects_name = gettext_noop("relations");
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (show_indexes)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("Table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)\n",
+						 relkind_str);
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c2138c26b9 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP, \dPi, \dPt */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..84c014993f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -249,6 +249,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[+]  [PATTERN]      list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[+] [PATTERN]      list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[+] [PATTERN]      list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a980f92e11..032c28ebdc 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3450,6 +3468,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
#33Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#11)
Re: ToDo: show size of partitioned table

On 2018-Jul-23, Pavel Stehule wrote:

p.s. Another patch can be replacement of relation type from "table" to
"partitioned table"

postgres=# \dt+
List of relations
+--------+------------+-------------------+-------+---------+-------------+
| Schema |    Name    |       Type        | Owner |  Size   | Description |
+--------+------------+-------------------+-------+---------+-------------+
| public | data       | partitioned table | pavel | 0 bytes |             |
| public | data_2016  | table             | pavel | 15 MB   |             |
| public | data_2017  | table             | pavel | 15 MB   |             |
| public | data_other | table             | pavel | 11 MB   |             |
+--------+------------+-------------------+-------+---------+-------------+
(4 rows)

I think this is a clear improvement. The term "table" was introduced
for this case by f0e44751d7 ("Implement table partitioning.") and now
the author of that commit supports this change. I used the term "index"
for partitioned indexes originally because I was copying the existing
term, but now I too think they should say "partitioned indexes" instead,
because they are different enough objects from plain indexes.

To be certain I'm not going against some old decision, I digged up
Amit's old patches. Turns out he submitted psql's describe.c using the
term "partitioned table" on August 10th [1]/messages/by-id/ad16e2f5-fc7c-cc2d-333a-88d4aa446f96@lab.ntt.co.jp and then based on a
discussion where Robert suggested calling these new objects "partition
roots" instead to avoid confusion, it was changed to "table" in the next
submission on August 26th [2]/messages/by-id/169708f6-6e5a-18d1-707b-1b323e4a6baf@lab.ntt.co.jp. It seems the right call to have used the
term "table" in many places (rather than "partition roots"), but at
least in psql's \dt it seems extremely useful to show the type as
"partitioned table" instead, because it is one place where the
distinction is clearly useful.

In this thread there have been no contrary votes, so I'm pushing this
part soon.

[1]: /messages/by-id/ad16e2f5-fc7c-cc2d-333a-88d4aa446f96@lab.ntt.co.jp
[2]: /messages/by-id/169708f6-6e5a-18d1-707b-1b323e4a6baf@lab.ntt.co.jp

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#34Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Alvaro Herrera (#33)
Re: ToDo: show size of partitioned table

On 2018/11/19 11:17, Alvaro Herrera wrote:

On 2018-Jul-23, Pavel Stehule wrote:

p.s. Another patch can be replacement of relation type from "table" to
"partitioned table"

postgres=# \dt+
List of relations
+--------+------------+-------------------+-------+---------+-------------+
| Schema |    Name    |       Type        | Owner |  Size   | Description |
+--------+------------+-------------------+-------+---------+-------------+
| public | data       | partitioned table | pavel | 0 bytes |             |
| public | data_2016  | table             | pavel | 15 MB   |             |
| public | data_2017  | table             | pavel | 15 MB   |             |
| public | data_other | table             | pavel | 11 MB   |             |
+--------+------------+-------------------+-------+---------+-------------+
(4 rows)

I think this is a clear improvement. The term "table" was introduced
for this case by f0e44751d7 ("Implement table partitioning.") and now
the author of that commit supports this change. I used the term "index"
for partitioned indexes originally because I was copying the existing
term, but now I too think they should say "partitioned indexes" instead,
because they are different enough objects from plain indexes.

To be certain I'm not going against some old decision, I digged up
Amit's old patches. Turns out he submitted psql's describe.c using the
term "partitioned table" on August 10th [1] and then based on a
discussion where Robert suggested calling these new objects "partition
roots" instead to avoid confusion, it was changed to "table" in the next
submission on August 26th [2]. It seems the right call to have used the
term "table" in many places (rather than "partition roots"), but at
least in psql's \dt it seems extremely useful to show the type as
"partitioned table" instead, because it is one place where the
distinction is clearly useful.

In this thread there have been no contrary votes, so I'm pushing this
part soon.

[1] /messages/by-id/ad16e2f5-fc7c-cc2d-333a-88d4aa446f96@lab.ntt.co.jp
[2] /messages/by-id/169708f6-6e5a-18d1-707b-1b323e4a6baf@lab.ntt.co.jp

Yeah, I agree that showing "partitioned table" for partitioned tables in
this case is helpful.

Earlier on this thread [1]/messages/by-id/5474c8b6-04e7-1afc-97b6-adb7471c2c71@lab.ntt.co.jp, I had expressed a slight concern about the
consistency of mentioning "partitioned" in various outputs, because many
error messages say "table" even if the table is partitioned. But now I
think that it's orthogonal. We should show "partitioned" where it is helpful.

Thanks,
Amit

[1]: /messages/by-id/5474c8b6-04e7-1afc-97b6-adb7471c2c71@lab.ntt.co.jp
/messages/by-id/5474c8b6-04e7-1afc-97b6-adb7471c2c71@lab.ntt.co.jp

#35Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#33)
1 attachment(s)
Re: ToDo: show size of partitioned table

On Sun, Nov 18, 2018 at 11:17:37PM -0300, Alvaro Herrera wrote:

To be certain I'm not going against some old decision, I digged up
Amit's old patches. Turns out he submitted psql's describe.c using the
term "partitioned table" on August 10th [1] and then based on a
discussion where Robert suggested calling these new objects "partition
roots" instead to avoid confusion, it was changed to "table" in the next
submission on August 26th [2]. It seems the right call to have used the
term "table" in many places (rather than "partition roots"), but at
least in psql's \dt it seems extremely useful to show the type as
"partitioned table" instead, because it is one place where the
distinction is clearly useful.

+1.

In this thread there have been no contrary votes, so I'm pushing this
part soon.

[1] /messages/by-id/ad16e2f5-fc7c-cc2d-333a-88d4aa446f96@lab.ntt.co.jp
[2] /messages/by-id/169708f6-6e5a-18d1-707b-1b323e4a6baf@lab.ntt.co.jp

Sorry for degressing, but could you also update \di at the same time so
as it shows "partitioned index"? listTables() should be switched to use
partitioned tables and partitioned indexes, and permissionsList() has a
reference to partitioned tables. While on it, this gives the attached..
--
Michael

Attachments:

psql-partition.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4ca0db1d0c..8e06097442 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -955,7 +955,7 @@ permissionsList(const char *pattern)
 					  gettext_noop("materialized view"),
 					  gettext_noop("sequence"),
 					  gettext_noop("foreign table"),
-					  gettext_noop("table"),	/* partitioned table */
+					  gettext_noop("partitioned table"),
 					  gettext_noop("Type"));
 
 	printACLColumn(&buf, "c.relacl");
@@ -3524,8 +3524,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 					  gettext_noop("sequence"),
 					  gettext_noop("special"),
 					  gettext_noop("foreign table"),
-					  gettext_noop("table"),	/* partitioned table */
-					  gettext_noop("index"),	/* partitioned index */
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
 					  gettext_noop("Type"),
 					  gettext_noop("Owner"));
 
#36Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#35)
Re: ToDo: show size of partitioned table

po 19. 11. 2018 v 3:42 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Sun, Nov 18, 2018 at 11:17:37PM -0300, Alvaro Herrera wrote:

To be certain I'm not going against some old decision, I digged up
Amit's old patches. Turns out he submitted psql's describe.c using the
term "partitioned table" on August 10th [1] and then based on a
discussion where Robert suggested calling these new objects "partition
roots" instead to avoid confusion, it was changed to "table" in the next
submission on August 26th [2]. It seems the right call to have used the
term "table" in many places (rather than "partition roots"), but at
least in psql's \dt it seems extremely useful to show the type as
"partitioned table" instead, because it is one place where the
distinction is clearly useful.

+1.

In this thread there have been no contrary votes, so I'm pushing this
part soon.

[1]

/messages/by-id/ad16e2f5-fc7c-cc2d-333a-88d4aa446f96@lab.ntt.co.jp

[2]

/messages/by-id/169708f6-6e5a-18d1-707b-1b323e4a6baf@lab.ntt.co.jp

Sorry for degressing, but could you also update \di at the same time so
as it shows "partitioned index"? listTables() should be switched to use
partitioned tables and partitioned indexes, and permissionsList() has a
reference to partitioned tables. While on it, this gives the attached..

It has sense

Pavel

--

Show quoted text

Michael

#37Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#36)
Re: ToDo: show size of partitioned table

On Mon, Nov 19, 2018 at 09:33:58AM +0100, Pavel Stehule wrote:

po 19. 11. 2018 v 3:42 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

Sorry for degressing, but could you also update \di at the same time so
as it shows "partitioned index"? listTables() should be switched to use
partitioned tables and partitioned indexes, and permissionsList() has a
reference to partitioned tables. While on it, this gives the attached..

It has sense

For the archive's sake, d56e0fde has been committed for this purpose.
--
Michael

#38Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#32)
Re: ToDo: show size of partitioned table

On Mon, Nov 05, 2018 at 11:43:16AM +0100, Pavel Stehule wrote:

should be fixed now.

Here are some notes on the last version.

+ " FROM pg_inherits i\n"
Missing schema qualification.

+           case 'P':
+               if (cmd[2] == 'i')
+                   success = listPartitions(pattern, show_verbose,
true, false);
+               else if (cmd[2] == 't')
+                   success = listPartitions(pattern, show_verbose,
false, true);
+               else if (cmd[2] == '+' || cmd[2] == '\0')
+                   success = listPartitions(pattern, show_verbose,
false, false);
+               else
+                   status = PSQL_CMD_UNKNOWN;
+               break;
The style is heavy.  Perhaps it would be cleaner to have a
switch/case..  Not a big deal visibly.  show_indexes is true only if the
subcommand is 'i'.  show_tables is true only if the subcommand is 't'.

Using "\dP" with a pattern matching a partitioned index should show a
partitioned index, no? As far as I know, a partitioned relation can be
either an index or a table.

Testing the feature, \dP shows all partitioned relations, still does not
show the relationship when multiple levels are used. Could it make
sense to also show the direct parent of a partitioned table when
verbose mode is used?

Could it be possible to have tests for \dP, \dPi and \dPt with matching
patterns? You could just place that in one of the existing tests where
there are partitioned tables and indexes.
--
Michael

#39Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Michael Paquier (#38)
Re: ToDo: show size of partitioned table

On 2018/11/20 16:50, Michael Paquier wrote:

Testing the feature, \dP shows all partitioned relations, still does not
show the relationship when multiple levels are used. Could it make
sense to also show the direct parent of a partitioned table when
verbose mode is used?

Yeah. I think it would make sense for \dP output to have an additional
column(s) for partitioning-specific information if we're building a
special command for partitioning anyway.

Thanks,
Amit

#40Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#39)
Re: ToDo: show size of partitioned table

út 20. 11. 2018 v 9:14 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

On 2018/11/20 16:50, Michael Paquier wrote:

Testing the feature, \dP shows all partitioned relations, still does not
show the relationship when multiple levels are used. Could it make
sense to also show the direct parent of a partitioned table when
verbose mode is used?

Yeah. I think it would make sense for \dP output to have an additional
column(s) for partitioning-specific information if we're building a
special command for partitioning anyway.

I can imagine to count columns, or levels columns

Regards

Pavel

Show quoted text

Thanks,
Amit

#41Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#38)
1 attachment(s)
Re: ToDo: show size of partitioned table

út 20. 11. 2018 v 8:50 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Mon, Nov 05, 2018 at 11:43:16AM +0100, Pavel Stehule wrote:

should be fixed now.

Here are some notes on the last version.

+ " FROM pg_inherits i\n"
Missing schema qualification.

fixed

+           case 'P':
+               if (cmd[2] == 'i')
+                   success = listPartitions(pattern, show_verbose,
true, false);
+               else if (cmd[2] == 't')
+                   success = listPartitions(pattern, show_verbose,
false, true);
+               else if (cmd[2] == '+' || cmd[2] == '\0')
+                   success = listPartitions(pattern, show_verbose,
false, false);
+               else
+                   status = PSQL_CMD_UNKNOWN;
+               break;
The style is heavy.  Perhaps it would be cleaner to have a
switch/case..  Not a big deal visibly.  show_indexes is true only if the
subcommand is 'i'.  show_tables is true only if the subcommand is 't'.

Using "\dP" with a pattern matching a partitioned index should show a
partitioned index, no? As far as I know, a partitioned relation can be
either an index or a table.

I don't think

\dP shows uses pg_total_relation_size as size function, and then we should
to display just tables, but with total size.

I don't see a sense to show indexes and tables too, more when we show total
relation size - see description for total relation size

"total disk space usage for the specified table and associated indexes"

Testing the feature, \dP shows all partitioned relations, still does not
show the relationship when multiple levels are used. Could it make
sense to also show the direct parent of a partitioned table when
verbose mode is used?

it is expected - you got one number for one partitioned table. I agree, so
can be interesting to see agregated sizes per partitioning hierarchy - but
in this moment I cannot to imagine form of the result.

any table can have different number of levels - so you can get different
number of values.

Could it be possible to have tests for \dP, \dPi and \dPt with matching
patterns? You could just place that in one of the existing tests where
there are partitioned tables and indexes.

I did it

see assigned patch, please.

Regards

Pavel

Show quoted text

--
Michael

Attachments:

psql-dP-5.patchtext/x-patch; charset=US-ASCII; name=psql-dP-5.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6e6d0f42d1..2166a25ba6 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,53 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ee88e1ca5c..d5c9b19ec6 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,27 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				switch (cmd[2])
+				{
+					case 'i':
+						/* show indexes only */
+						success = listPartitions(pattern, show_verbose, true, false);
+						break;
+					case 't':
+						/* show tables only */
+						success = listPartitions(pattern, show_verbose, false, true);
+						break;
+					case '+':
+					case '\0':
+						/* show relations - tables + indexes */
+						success = listPartitions(pattern, show_verbose, false, false);
+						break;
+					default:
+						status = PSQL_CMD_UNKNOWN;
+						break;
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 0a181b01d9..6c9c980138 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,183 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		size_function = "pg_total_relation_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("relation");
+		objects_name = gettext_noop("relations");
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (show_indexes)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("Table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_catalog.pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_catalog.pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)\n",
+						 relkind_str);
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c2138c26b9 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP, \dPi, \dPt */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..84c014993f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -249,6 +249,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[+]  [PATTERN]      list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[+] [PATTERN]      list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[+] [PATTERN]      list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dbd555166..7eb43f8bf3 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3447,6 +3465,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 3818cfea7e..ab0cda7474 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -3243,3 +3243,67 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP
+  List of partitioned relations
+ Schema |      Name       | Owner 
+--------+-----------------+-------
+ public | mlparted        | pavel
+ public | mlparted1       | pavel
+ public | mlparted_def    | pavel
+ public | testpart_apple  | pavel
+ public | testpart_orange | pavel
+(5 rows)
+
+\dPt
+    List of partitioned tables
+ Schema |      Name       | Owner 
+--------+-----------------+-------
+ public | mlparted        | pavel
+ public | mlparted1       | pavel
+ public | mlparted_def    | pavel
+ public | testpart_apple  | pavel
+ public | testpart_orange | pavel
+(5 rows)
+
+\dPi
+               List of partitioned indexes
+ Schema |         Name          | Owner |      Table      
+--------+-----------------------+-------+-----------------
+ public | testpart_apple_index  | pavel | testpart_apple
+ public | testpart_orange_index | pavel | testpart_orange
+(2 rows)
+
+\dP *apple*
+  List of partitioned relations
+ Schema |      Name      | Owner 
+--------+----------------+-------
+ public | testpart_apple | pavel
+(1 row)
+
+\dPt *apple*
+   List of partitioned tables
+ Schema |      Name      | Owner 
+--------+----------------+-------
+ public | testpart_apple | pavel
+(1 row)
+
+\dPi *apple*
+              List of partitioned indexes
+ Schema |         Name         | Owner |     Table      
+--------+----------------------+-------+----------------
+ public | testpart_apple_index | pavel | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index b45da9bb8d..329e1320c8 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -688,3 +688,28 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP
+\dPt
+\dPi
+
+\dP *apple*
+\dPt *apple*
+\dPi *apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
#42Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#41)
Re: ToDo: show size of partitioned table

Hmm, these tests are not going to work, because they have "pavel" in the
expected output.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#43Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#42)
1 attachment(s)
Re: ToDo: show size of partitioned table

st 21. 11. 2018 v 17:21 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

Hmm, these tests are not going to work, because they have "pavel" in the
expected output.

I was blind, thank you for check

fixed

Regards

Pavel

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

psql-dP-6.patchtext/x-patch; charset=US-ASCII; name=psql-dP-6.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6e6d0f42d1..2166a25ba6 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1635,6 +1635,53 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ee88e1ca5c..d5c9b19ec6 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,27 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				switch (cmd[2])
+				{
+					case 'i':
+						/* show indexes only */
+						success = listPartitions(pattern, show_verbose, true, false);
+						break;
+					case 't':
+						/* show tables only */
+						success = listPartitions(pattern, show_verbose, false, true);
+						break;
+					case '+':
+					case '\0':
+						/* show relations - tables + indexes */
+						success = listPartitions(pattern, show_verbose, false, false);
+						break;
+					default:
+						status = PSQL_CMD_UNKNOWN;
+						break;
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 0a181b01d9..6c9c980138 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,183 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		size_function = "pg_total_relation_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("relation");
+		objects_name = gettext_noop("relations");
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (show_indexes)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("Table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_catalog.pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_catalog.pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)\n",
+						 relkind_str);
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c2138c26b9 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP, \dPi, \dPt */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 586aebddd3..84c014993f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -167,7 +167,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(125, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -249,6 +249,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[+]  [PATTERN]      list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[+] [PATTERN]      list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[+] [PATTERN]      list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dbd555166..7eb43f8bf3 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3447,6 +3465,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 3818cfea7e..0198b8ecbb 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -3243,3 +3243,41 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create role testrole_partitioning;
+set role to testrole_partitioning;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+          List of partitioned relations
+ Schema |      Name      |         Owner         
+--------+----------------+-----------------------
+ public | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPt test*apple*
+           List of partitioned tables
+ Schema |      Name      |         Owner         
+--------+----------------+-----------------------
+ public | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                      List of partitioned indexes
+ Schema |         Name         |         Owner         |     Table      
+--------+----------------------+-----------------------+----------------
+ public | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index b45da9bb8d..272504f375 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -688,3 +688,30 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create role testrole_partitioning;
+set role to testrole_partitioning;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+set role to default;
+drop role testrole_partitioning;
#44Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#43)
Re: ToDo: show size of partitioned table

On Wed, Nov 21, 2018 at 05:37:33PM +0100, Pavel Stehule wrote:

st 21. 11. 2018 v 17:21 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

Hmm, these tests are not going to work, because they have "pavel" in the
expected output.

I was blind, thank you for check

+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
There are already a bunch of partition relations with multiple levels
created as part of the regression tests, so instead of creating more of
those, I would suggest to test \dP and \dPt in create_table.sql, and
\dPi in indexing.sql (please make sure to add tests for \dP with
partitioned indexes as well).

I think that you should really add the direct parent of a partition in
at least the verbose output, now for multiple partition levels things
are confusing in my opinion. For example with such a schema:
CREATE TABLE parent_tab (id int) PARTITION BY RANGE (id);
CREATE INDEX parent_index ON parent_tab (id);
CREATE TABLE child_0_10 PARTITION OF parent_tab
FOR VALUES FROM (0) TO (10);
CREATE TABLE child_10_20 PARTITION OF parent_tab
FOR VALUES FROM (10) TO (20);
CREATE TABLE child_20_30 PARTITION OF parent_tab
FOR VALUES FROM (20) TO (30);
INSERT INTO parent_tab VALUES (generate_series(0,29));
CREATE TABLE child_30_40 PARTITION OF parent_tab
FOR VALUES FROM (30) TO (40)
PARTITION BY RANGE(id);
CREATE TABLE child_30_35 PARTITION OF child_30_40
FOR VALUES FROM (30) TO (35);
CREATE TABLE child_35_40 PARTITION OF child_30_40
FOR VALUES FROM (35) TO (40);
INSERT INTO parent_tab VALUES (generate_series(30,39));

Then with \dP+ I got that:
=# \dP+
List of partitioned relations
Schema | Name | Owner | Size | Description
--------+-------------+--------+--------+-------------
public | child_30_40 | ioltas | 48 kB |
public | parent_tab | ioltas | 120 kB |
(2 rows)
Showing the parent partition looks like a pretty important to me as I
would expect multi-level partitions to be a frequent case (perhaps it
should show up as well in the non-verbose output?). The field should be
NULL if the relation is the top of the tree.

Again, with the previous schema:
=# \dPi *idx
List of partitioned indexes
Schema | Name | Owner | Table
--------+--------------------+--------+-------------
public | child_30_40_id_idx | ioltas | child_30_40
(1 row)
=# \dP *idx
Did not find any partitioned relations named "*idx"
I would have expected in the second case to have the partitioned
*relations* showing up in the output, and a relation can be an index as
well if the pattern matches.

Could you please address those problems first? The basic shape of the
patch with the three new sub-commands is fine I think, so we can go
ahead with that, but the two problems reported are blockers in my
opinion.
--
Michael

#45Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#44)
Re: ToDo: show size of partitioned table

čt 22. 11. 2018 v 1:51 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Wed, Nov 21, 2018 at 05:37:33PM +0100, Pavel Stehule wrote:

st 21. 11. 2018 v 17:21 odesílatel Alvaro Herrera <

alvherre@2ndquadrant.com>

napsal:

Hmm, these tests are not going to work, because they have "pavel" in the
expected output.

I was blind, thank you for check

+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
There are already a bunch of partition relations with multiple levels
created as part of the regression tests, so instead of creating more of
those, I would suggest to test \dP and \dPt in create_table.sql, and
\dPi in indexing.sql (please make sure to add tests for \dP with
partitioned indexes as well).

I think that you should really add the direct parent of a partition in
at least the verbose output, now for multiple partition levels things
are confusing in my opinion. For example with such a schema:
CREATE TABLE parent_tab (id int) PARTITION BY RANGE (id);
CREATE INDEX parent_index ON parent_tab (id);
CREATE TABLE child_0_10 PARTITION OF parent_tab
FOR VALUES FROM (0) TO (10);
CREATE TABLE child_10_20 PARTITION OF parent_tab
FOR VALUES FROM (10) TO (20);
CREATE TABLE child_20_30 PARTITION OF parent_tab
FOR VALUES FROM (20) TO (30);
INSERT INTO parent_tab VALUES (generate_series(0,29));
CREATE TABLE child_30_40 PARTITION OF parent_tab
FOR VALUES FROM (30) TO (40)
PARTITION BY RANGE(id);
CREATE TABLE child_30_35 PARTITION OF child_30_40
FOR VALUES FROM (30) TO (35);
CREATE TABLE child_35_40 PARTITION OF child_30_40
FOR VALUES FROM (35) TO (40);
INSERT INTO parent_tab VALUES (generate_series(30,39));

Then with \dP+ I got that:
=# \dP+
List of partitioned relations
Schema | Name | Owner | Size | Description
--------+-------------+--------+--------+-------------
public | child_30_40 | ioltas | 48 kB |
public | parent_tab | ioltas | 120 kB |
(2 rows)
Showing the parent partition looks like a pretty important to me as I
would expect multi-level partitions to be a frequent case (perhaps it
should show up as well in the non-verbose output?). The field should be
NULL if the relation is the top of the tree.

it looks like bug for me much more.

your example - on my comp

                               List of relations
+--------+-------------+-------------------+-------+------------+-------------+
| Schema |    Name     |       Type        | Owner |    Size    |
Description |
+--------+-------------+-------------------+-------+------------+-------------+
| public | child_0_10  | table             | pavel | 8192 bytes
|             |
| public | child_10_20 | table             | pavel | 8192 bytes
|             |
| public | child_20_30 | table             | pavel | 8192 bytes
|             |
| public | child_30_35 | table             | pavel | 8192 bytes
|             |
| public | child_30_40 | partitioned table | pavel | 0 bytes
|             |
| public | child_35_40 | table             | pavel | 8192 bytes
|             |
| public | parent_tab  | partitioned table | pavel | 0 bytes
|             |
+--------+-------------+-------------------+-------+------------+-------------+
(7 rows)

there is about 5x 8KB data .. 40KB

But in views I got

              List of partitioned tables
+--------+-------------+-------+-------+-------------+
| Schema |    Name     | Owner | Size  | Description |
+--------+-------------+-------+-------+-------------+
| public | child_30_40 | pavel | 16 kB |             |
| public | parent_tab  | pavel | 40 kB |             |
+--------+-------------+-------+-------+-------------+
(2 rows)

there is 16KB more, what is really messy.

I think so most correct is removing child_30_40 from the report.

test=# SELECT n.nspname as "Schema",
  c.relname as "Name",
  pg_catalog.pg_get_userbyid(c.relowner) as "Owner",
  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(relid)))
     FROM pg_catalog.pg_partition_tree(c.oid)) AS "Size",
  pg_catalog.obj_description(c.oid, 'pg_class') as "Description"
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('p') and not c.relispartition
      AND n.nspname <> 'pg_catalog'
      AND n.nspname <> 'information_schema'
      AND n.nspname !~ '^pg_toast'
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 1,2;
+--------+------------+-------+-------+-------------+
| Schema |    Name    | Owner | Size  | Description |
+--------+------------+-------+-------+-------------+
| public | parent_tab | pavel | 40 kB |             |
+--------+------------+-------+-------+-------------+
(1 row)

I afraid of unreadable result if we allow overlap in report. I think so can
be strange if some disk space will be reported 2x or more times in one
report. Unfortunately It means so some information will be hidden. In this
moment I prefer readability and simple meaning.

I am not strong in this topics. Another possibility is show parent (this
should be displayed every time, without it it is messy).

This query is much more complex, but the result is more informative

SELECT n.nspname as "Schema",
c.relname as "Name",
n2.nspname as "Parent schema",
c2.relname as "Parent name",
pg_catalog.pg_get_userbyid(c.relowner) as "Owner",
s.max as "Hiearchy deep",
s.size as "Size",
pg_catalog.obj_description(c.oid, 'pg_class') as "Description"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid
LEFT JOIN pg_catalog.pg_class c2 ON c2.oid = i.inhparent
LEFT JOIN pg_catalog.pg_namespace n2 ON n2.oid = c2.relnamespace,
LATERAL (SELECT max(level),
pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(relid))) as size
FROM pg_catalog.pg_partition_tree(c.oid)) s
WHERE c.relkind IN ('p')
AND n.nspname <> 'pg_catalog'
AND n.nspname <> 'information_schema'
AND n.nspname !~ '^pg_toast'
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 1,2;

+--------+-------------+---------------+-------------+-------+---------------+-------+-------------+
| Schema |    Name     | Parent schema | Parent name | Owner | Hiearchy
deep | Size  | Description |
+--------+-------------+---------------+-------------+-------+---------------+-------+-------------+
| public | child_30_40 | public        | parent_tab  | pavel |
1 | 16 kB |             |
| public | parent_tab  |               |             | pavel |
2 | 40 kB |             |
+--------+-------------+---------------+-------------+-------+---------------+-------+-------------+
(2 rows)

Still I prefer to not show nested partitioned tables for simplicity,
readability reasons. Displaying nested objects in one table doesn't look
like good idea for me. But I am ready to accept different common opinion.

Still do you think so variant with parent should be preferred?

Again, with the previous schema:
=# \dPi *idx
List of partitioned indexes
Schema | Name | Owner | Table
--------+--------------------+--------+-------------
public | child_30_40_id_idx | ioltas | child_30_40
(1 row)
=# \dP *idx
Did not find any partitioned relations named "*idx"
I would have expected in the second case to have the partitioned
*relations* showing up in the output, and a relation can be an index as
well if the pattern matches.

I think so it is correct - I don't would to see the index here, because
index size is calculated by total_relation_size already.

Here my position is strong. \dP for me doesn't mean "tables or indexes" -
it means "partition tables with total relation size". I don't see any sense
to show tables and indexes in one report.

Regards

Pavel

Could you please address those problems first? The basic shape of the
patch with the three new sub-commands is fine I think, so we can go
ahead with that, but the two problems reported are blockers in my
opinion.

--

Show quoted text

Michael

#46Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#45)
Re: ToDo: show size of partitioned table

On Thu, Nov 22, 2018 at 12:42:14PM +0100, Pavel Stehule wrote:

Here my position is strong. \dP for me doesn't mean "tables or
indexes" - it means "partition tables with total relation size". I
don't see any sense to show tables and indexes in one report.

Please let me disagree on that point. \dP, \dPt and \dPi are commands
able to show information about respectively partitioned relations,
partitioned tables and partitioned indexes, which is not something only
related to the size of those partitions. Showing only the level of a
relation in its hierarchy may be useful, but that's confusing for the
user without knowing its direct parent or its top-most parent. For
multiple levels, the direct parent without the number in the hierarchy
seems enough to me. I may be of course wrong in designing those
concepts.
--
Michael

#47Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#46)
Re: ToDo: show size of partitioned table

čt 22. 11. 2018 v 15:29 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Thu, Nov 22, 2018 at 12:42:14PM +0100, Pavel Stehule wrote:

Here my position is strong. \dP for me doesn't mean "tables or
indexes" - it means "partition tables with total relation size". I
don't see any sense to show tables and indexes in one report.

Please let me disagree on that point. \dP, \dPt and \dPi are commands
able to show information about respectively partitioned relations,
partitioned tables and partitioned indexes, which is not something only
related to the size of those partitions. Showing only the level of a
relation in its hierarchy may be useful, but that's confusing for the
user without knowing its direct parent or its top-most parent. For
multiple levels, the direct parent without the number in the hierarchy
seems enough to me. I may be of course wrong in designing those
concepts.

There are open two points:

1. display hierarchy of partitioned structures.
2. what should be displayed by \dP command.

@1 I agree so this information can be interesting and useful. But I have a
problem with consistency of this report. When result is table, then I think
so we can introduce, and should to introduce some new special report for
command - maybe \dPh

that can show hiearchy of one partitioned table (the table name should be
required)

I think so can be much more readable to have special report like

\dPh parent_tab
parent_tab
-> direct partitions 24kB
-> child_30_40
-> direct partitions 16kB

This is some what i can read, and I see (very naturally) the hierarchy of
partitions and the relations between

I have not feel well when I see in one report numbers 40 and 16, I see much
more comfortable when I see 24 and 16, but for this I need a different
perspective

What do you think about it?

Show quoted text

--
Michael

#48Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#47)
Re: ToDo: show size of partitioned table

On Thu, Nov 22, 2018 at 03:47:11PM +0100, Pavel Stehule wrote:

I have not feel well when I see in one report numbers 40 and 16, I see much
more comfortable when I see 24 and 16, but for this I need a different
perspective

What do you think about it?

Maybe, my thought is that this would be a separate patch, and that we
had better keep it simple for now because that's complicated enough :)

So I would rather have a wrapper on top of pg_partition_tree which is
useful for the user with matching patterns, which shows roughly:
- the size of the whole partition tree from the root.
- its direct parent if any. Potentially in the verbose output. This
depends on how much users have multi-level partitions. My bet would be
not that much, but more input from others is welcome.
- perhaps its level in the hierarchy, if integrated most likely as part
of the verbose output.

Then with the set of commands, have a mapping close to how \d treats
indexes and relations:
- \dp shows information about partitioned tables or indexes, if no pattern
is defined tables show up. If a partitioned index pattern shows up,
then index information is displayed.
- \dpi and \dpt for respectively partitioned indexes and tables.
--
Michael

#49Pavel Stehule
pavel.stehule@gmail.com
In reply to: Michael Paquier (#48)
Re: ToDo: show size of partitioned table

čt 29. 11. 2018 v 7:58 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Thu, Nov 22, 2018 at 03:47:11PM +0100, Pavel Stehule wrote:

I have not feel well when I see in one report numbers 40 and 16, I see

much

more comfortable when I see 24 and 16, but for this I need a different
perspective

What do you think about it?

Maybe, my thought is that this would be a separate patch, and that we
had better keep it simple for now because that's complicated enough :)

any time, the behave should be consistent.

I agree, so there can be another patch, that reports more about partitioned
table for \d+

So I would rather have a wrapper on top of pg_partition_tree which is
useful for the user with matching patterns, which shows roughly:
- the size of the whole partition tree from the root.
- its direct parent if any. Potentially in the verbose output. This
depends on how much users have multi-level partitions. My bet would be
not that much, but more input from others is welcome.
- perhaps its level in the hierarchy, if integrated most likely as part
of the verbose output.

Then with the set of commands, have a mapping close to how \d treats
indexes and relations:
- \dp shows information about partitioned tables or indexes, if no pattern
is defined tables show up. If a partitioned index pattern shows up,
then index information is displayed.

ok

- \dpi and \dpt for respectively partitioned indexes and tables.

--

Show quoted text

Michael

#50Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#49)
1 attachment(s)
Re: ToDo: show size of partitioned table

Hi

pá 30. 11. 2018 v 20:06 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:

čt 29. 11. 2018 v 7:58 odesílatel Michael Paquier <michael@paquier.xyz>
napsal:

On Thu, Nov 22, 2018 at 03:47:11PM +0100, Pavel Stehule wrote:

I have not feel well when I see in one report numbers 40 and 16, I see

much

more comfortable when I see 24 and 16, but for this I need a different
perspective

What do you think about it?

Maybe, my thought is that this would be a separate patch, and that we
had better keep it simple for now because that's complicated enough :)

any time, the behave should be consistent.

I agree, so there can be another patch, that reports more about
partitioned table for \d+

So I would rather have a wrapper on top of pg_partition_tree which is
useful for the user with matching patterns, which shows roughly:
- the size of the whole partition tree from the root.
- its direct parent if any. Potentially in the verbose output. This
depends on how much users have multi-level partitions. My bet would be
not that much, but more input from others is welcome.
- perhaps its level in the hierarchy, if integrated most likely as part
of the verbose output.

Then with the set of commands, have a mapping close to how \d treats
indexes and relations:
- \dp shows information about partitioned tables or indexes, if no pattern
is defined tables show up. If a partitioned index pattern shows up,
then index information is displayed.

ok

- \dpi and \dpt for respectively partitioned indexes and tables.

new update of this patch

changes:

1. only root partitioned tables are displayed - you can see quickly total
allocated space. It is not duplicated due nested partitions.

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

2. \dP without pattern shows root partitioned tables + total relation size.
When pattern is defined, then shows tables and indexes + table size

postgres=# \dP+
List of partitioned relations
┌────────┬────────────┬───────┬────────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪════════════╪═══════╪════════╪═════════════╡
│ public │ parent_tab │ pavel │ 120 kB │ │
└────────┴────────────┴───────┴────────┴─────────────┘
(1 row)

postgres=# \dP+ *
List of partitioned relations or indexes
┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Type │ Table │ Size │
Description │
╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡
│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │
│ public │ parent_tab │ pavel │ partitioned table │ │ 40 kB
│ │
└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘
(2 rows)

postgres=# \dP+ *index
List of partitioned relations or indexes
┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Type │ Table │ Size │
Description │
╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡
│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │
└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘
(1 row)

Regards

Pavel

Show quoted text

--

Michael

Attachments:

psql-dP-7.patchtext/x-patch; charset=US-ASCII; name=psql-dP-7.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6c76cf2f00..626c89d055 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1645,6 +1645,53 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 55315fe43b..9bdb610e94 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,31 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				switch (cmd[2])
+				{
+					case 'i':
+						/* show indexes only */
+						success = listPartitions(pattern, show_verbose, true, false);
+						break;
+					case 't':
+						/* show tables only */
+						success = listPartitions(pattern, show_verbose, false, true);
+						break;
+					case '+':
+					case '\0':
+						/*
+						 * show relations - when there are not pattern, then it shows
+						 * tables with total relation size, else where it shows tables
+						 * and indexes.
+						 */
+						success = listPartitions(pattern, show_verbose, false, false);
+						break;
+					default:
+						status = PSQL_CMD_UNKNOWN;
+						break;
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 0a181b01d9..92c57064f6 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,209 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			object_name = gettext_noop("relation");
+			objects_name = gettext_noop("relations");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			object_name = gettext_noop("relation or index");
+			objects_name = gettext_noop("relations or indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("Table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid\n"
+							  "              FROM pg_catalog.pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid\n"
+							  "              FROM pg_catalog.pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid))) FROM d) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid)) AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  size_function,
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s) AND NOT c.relispartition\n",
+						 relkind_str);
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..c2138c26b9 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP, \dPi, \dPt */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 2e9fe760eb..2d3e0404ef 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,7 +168,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -250,6 +250,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[+]  [PATTERN]      list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[+] [PATTERN]      list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[+] [PATTERN]      list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index fa44b2820b..f447fc9917 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3446,6 +3464,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 775b127121..7d71666910 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4539,3 +4539,83 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create role testrole_partitioning;
+set role to testrole_partitioning;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                          List of partitioned relations or indexes
+ Schema |         Name         |         Owner         |       Type        |     Table      
+--------+----------------------+-----------------------+-------------------+----------------
+ public | testpart_apple       | testrole_partitioning | partitioned table | 
+ public | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+           List of partitioned tables
+ Schema |      Name      |         Owner         
+--------+----------------+-----------------------
+ public | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                      List of partitioned indexes
+ Schema |         Name         |         Owner         |     Table      
+--------+----------------------+-----------------------+----------------
+ public | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt parent*
+         List of partitioned tables
+ Schema |    Name    |         Owner         
+--------+------------+-----------------------
+ public | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi parent*
+                List of partitioned indexes
+ Schema |     Name     |         Owner         |   Table    
+--------+--------------+-----------------------+------------
+ public | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+-- \dP cannot to test due unwanted partitioned tables from other tests in result
+\dP parent*
+                    List of partitioned relations or indexes
+ Schema |     Name     |         Owner         |       Type        |   Table    
+--------+--------------+-----------------------+-------------------+------------
+ public | parent_index | testrole_partitioning | partitioned index | parent_tab
+ public | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+drop table parent_tab cascade;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 1bb2a6e16d..862e80f61f 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1016,3 +1016,56 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create role testrole_partitioning;
+set role to testrole_partitioning;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt parent*
+\dPi parent*
+
+-- \dP cannot to test due unwanted partitioned tables from other tests in result
+\dP parent*
+
+drop table parent_tab cascade;
+
+set role to default;
+drop role testrole_partitioning;
#51Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#50)
Re: ToDo: show size of partitioned table

Hi,

Thank you for updating the patch.

On 2018/12/17 17:48, Pavel Stehule wrote:

new update of this patch

Documentation portion of this patch still contains some typos that I
mentioned before here:

/messages/by-id/1c83bb5c-47cd-d796-226c-e95795b05551@lab.ntt.co.jp

+ .. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+ ... If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+ ... If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.

In all of the three hunks:

the sum of size of -> the sum of "sizes" of

and a description -> and associated description

changes:

1. only root partitioned tables are displayed - you can see quickly total
allocated space. It is not duplicated due nested partitions.

+1

If one wants to see a non-root partitioned table's details, they can use
\dP+ <pattern>.

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

Too much complication maybe?

2. \dP without pattern shows root partitioned tables + total relation size.
When pattern is defined, then shows tables and indexes + table size

postgres=# \dP+
List of partitioned relations
┌────────┬────────────┬───────┬────────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪════════════╪═══════╪════════╪═════════════╡
│ public │ parent_tab │ pavel │ 120 kB │ │
└────────┴────────────┴───────┴────────┴─────────────┘
(1 row)

postgres=# \dP+ *
List of partitioned relations or indexes
┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Type │ Table │ Size │
Description │
╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡
│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │
│ public │ parent_tab │ pavel │ partitioned table │ │ 40 kB
│ │
└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘
(2 rows)

postgres=# \dP+ *index
List of partitioned relations or indexes
┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Type │ Table │ Size │
Description │
╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡
│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │
└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘
(1 row)

Looking at the patch:

+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);

It seems that objects_name and object_name need to be swapped between the
if and else blocks, and so do /* translator: ... */ comments.

if (pattern)
/* translator: object_name is "index", "table" or "relation" */
psql_error(..., object_name);
else
/* translator: objects_name is "indexes", "tables" or "relations" */
psql_error(..., objects_name);

That is, it should say, "Did not find any partitioned index/table/relation
named "foo" and "Did not find any partitioned indexes/tables/relations".

Thanks,
Amit

#52Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#51)
1 attachment(s)
Re: ToDo: show size of partitioned table

út 18. 12. 2018 v 8:49 odesílatel Amit Langote <
Langote_Amit_f8@lab.ntt.co.jp> napsal:

Hi,

Thank you for updating the patch.

On 2018/12/17 17:48, Pavel Stehule wrote:

new update of this patch

Documentation portion of this patch still contains some typos that I
mentioned before here:

/messages/by-id/1c83bb5c-47cd-d796-226c-e95795b05551@lab.ntt.co.jp

+ .. If the form <literal>\dP+</literal>
+        is used, the sum of size of related partitions (including the
+        table and indexes, if any) and a description
+        are also displayed.
+ ... If the form <literal>\dPi+</literal>
+        is used, the sum of size of related indexes and a description
+        are also displayed.
+ ... If the form <literal>\dPt+</literal>
+        is used, the sum of size of related tables and a description
+        are also displayed.

In all of the three hunks:

the sum of size of -> the sum of "sizes" of

and a description -> and associated description

fixed

changes:

1. only root partitioned tables are displayed - you can see quickly total
allocated space. It is not duplicated due nested partitions.

+1

If one wants to see a non-root partitioned table's details, they can use
\dP+ <pattern>.

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

Too much complication maybe?

I wrote it - the setup query is more complex, but not too much. I fixed the
size calculation, when nested partitions tables are visible - it calculate
partitions only from level1 group. Then the displayed size is same as total
size

postgres=# \dP+
List of partitioned relations
┌────────┬────────────┬───────┬────────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪════════════╪═══════╪════════╪═════════════╡
│ public │ parent_tab │ pavel │ 120 kB │ │
└────────┴────────────┴───────┴────────┴─────────────┘
(1 row)

postgres=# \dPn+
List of partitioned relations
┌────────┬─────────────┬───────┬─────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Size │ Description │
╞════════╪═════════════╪═══════╪═════════════╪═══════╪═════════════╡
│ public │ child_30_40 │ pavel │ parent_tab │ 48 kB │ │
│ public │ parent_tab │ pavel │ │ 72 kB │ │
└────────┴─────────────┴───────┴─────────────┴───────┴─────────────┘
(2 rows)

postgres=# \dPn+ *
List of partitioned relations or indexes
┌────────┬────────────────────┬───────┬───────────────────┬──────────────┬─────────────┬───────┬─────────────┐
│ Schema │ Name │ Owner │ Type │ Parent name │
On table │ Size │ Description │
╞════════╪════════════════════╪═══════╪═══════════════════╪══════════════╪═════════════╪═══════╪═════════════╡
│ public │ child_30_40 │ pavel │ partitioned table │ parent_tab
│ │ 16 kB │ │
│ public │ child_30_40_id_idx │ pavel │ partitioned index │ parent_index │
child_30_40 │ 32 kB │ │
│ public │ parent_index │ pavel │ partitioned index │ │
parent_tab │ 48 kB │ │
│ public │ parent_tab │ pavel │ partitioned table │
│ │ 24 kB │ │
└────────┴────────────────────┴───────┴───────────────────┴──────────────┴─────────────┴───────┴─────────────┘
(4 rows)

2. \dP without pattern shows root partitioned tables + total relation

size.

When pattern is defined, then shows tables and indexes + table size

postgres=# \dP+
List of partitioned relations
┌────────┬────────────┬───────┬────────┬─────────────┐
│ Schema │ Name │ Owner │ Size │ Description │
╞════════╪════════════╪═══════╪════════╪═════════════╡
│ public │ parent_tab │ pavel │ 120 kB │ │
└────────┴────────────┴───────┴────────┴─────────────┘
(1 row)

postgres=# \dP+ *
List of partitioned relations or indexes

┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐

│ Schema │ Name │ Owner │ Type │ Table │ Size

Description │

╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡

│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │
│ public │ parent_tab │ pavel │ partitioned table │ │ 40 kB
│ │

└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘

(2 rows)

postgres=# \dP+ *index
List of partitioned relations or indexes

┌────────┬──────────────┬───────┬───────────────────┬────────────┬───────┬─────────────┐

│ Schema │ Name │ Owner │ Type │ Table │ Size

Description │

╞════════╪══════════════╪═══════╪═══════════════════╪════════════╪═══════╪═════════════╡

│ public │ parent_index │ pavel │ partitioned index │ parent_tab │ 80 kB
│ │

└────────┴──────────────┴───────┴───────────────────┴────────────┴───────┴─────────────┘

(1 row)

Looking at the patch:

+               if (pattern)
+                       /* translator: objects_name is "indexes", "tables"
or "relations" */
+                       psql_error("Did not find any partitioned %s named
\"%s\".\n",
+                                          objects_name,
+                                          pattern);
+               else
+                       /* translator: object_name is "index", "table" or
"relation" */
+                       psql_error("Did not find any partitioned %s.\n",
+                                         object_name);

It seems that objects_name and object_name need to be swapped between the
if and else blocks, and so do /* translator: ... */ comments.

if (pattern)
/* translator: object_name is "index", "table" or "relation" */
psql_error(..., object_name);
else
/* translator: objects_name is "indexes", "tables" or "relations" */
psql_error(..., objects_name);

That is, it should say, "Did not find any partitioned index/table/relation
named "foo" and "Did not find any partitioned indexes/tables/relations".

fixed

I am sending updated patch

Regards

Pavel

Show quoted text

Thanks,
Amit

Attachments:

psql-dP-8.patchtext/x-patch; charset=US-ASCII; name=psql-dP-8.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6c76cf2f00..1639c3f979 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1645,6 +1645,71 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of sizes of related partitions (including the
+        table and indexes, if any) and associated description
+        are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of sizes of related indexes and associated
+        description are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of sizes of related tables and associated
+        description are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 55315fe43b..c70c422ee0 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,36 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					bool show_nested_partitions = strchr(cmd, 'n') ? true : false;
+
+					switch (cmd[2])
+					{
+						case 'i':
+							/* show indexes only */
+							success = listPartitions(pattern, show_verbose, true, false, show_nested_partitions);
+							break;
+						case 't':
+							/* show tables only */
+							success = listPartitions(pattern, show_verbose, false, true, show_nested_partitions);
+							break;
+						case '+':
+						case '\0':
+						case 'n':
+							/*
+							 * show relations - when there are not pattern, then it shows
+							 * tables with total relation size, else where it shows tables
+							 * and indexes.
+							 */
+							success = listPartitions(pattern, show_verbose, false, false, show_nested_partitions);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 0a181b01d9..2b62cb40e5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,241 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested_partitions)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static bool translate_columns[] = {false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			object_name = gettext_noop("relation");
+			objects_name = gettext_noop("relations");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			object_name = gettext_noop("relation or index");
+			objects_name = gettext_noop("relations or indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+		translate_columns[4] = true;
+	}
+
+	if (show_nested_partitions)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  (WITH RECURSIVE d\n"
+							  "        AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "              FROM pg_catalog.pg_inherits\n"
+							  "             WHERE inhparent = c.oid\n"
+							  "            UNION ALL\n"
+							  "            SELECT inhrelid, level + 1\n"
+							  "              FROM pg_catalog.pg_inherits i\n"
+							  "                   JOIN d ON i.inhparent = d.oid)\n"
+							  "         SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "oid)))\n"
+							  "         FROM d",
+							  size_function);
+
+			if (show_nested_partitions)
+				appendPQExpBuffer(&buf, "\n         WHERE d.level = 1");
+
+			appendPQExpBuffer(&buf,
+							  ") AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n  (SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "ppt.relid)))\n"
+							  "     FROM pg_catalog.pg_partition_tree(c.oid) ppt",
+							  size_function);
+
+			/*
+			 * Assign size just for directly assigned tables, when nested
+			 * partitions are visible
+			 */
+			if (show_nested_partitions)
+				appendPQExpBuffer(&buf, "\n     WHERE ppt.isleaf AND ppt.level = 1");
+
+			appendPQExpBuffer(&buf,
+							  ") AS \"%s\""
+							  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+							  gettext_noop("Size"),
+							  gettext_noop("Description"));
+		}
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (show_nested_partitions)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !show_nested_partitions ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+		else
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index a4cc5efae0..1cf3a13598 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP[n], \dPi[n], \dPt[n] */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested_partitions);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 2e9fe760eb..bf747b883e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,7 +168,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -250,6 +250,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index fa44b2820b..f447fc9917 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -452,6 +452,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -460,6 +469,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1332,7 +1350,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3446,6 +3464,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 775b127121..52bcb3f918 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4539,3 +4539,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                           List of partitioned relations or indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                     List of partitioned relations or indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+         List of partitioned relations
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                List of partitioned relations
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPn testpart.*
+                                List of partitioned relations or indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 1bb2a6e16d..540a416a7b 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1016,3 +1016,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#53Michael Paquier
michael@paquier.xyz
In reply to: Pavel Stehule (#52)
Re: ToDo: show size of partitioned table

On Wed, Dec 19, 2018 at 07:38:16AM +0100, Pavel Stehule wrote:

I am sending updated patch.

Moved to next CF.
--
Michael

#54Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#52)
Re: ToDo: show size of partitioned table

Hi Pavel,

Thanks for sending the updated patch.

On 2018/12/19 15:38, Pavel Stehule wrote:

út 18. 12. 2018 v 8:49 odesílatel Amit Langote <

On 2018/12/17 17:48, Pavel Stehule wrote:

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

Too much complication maybe?

I wrote it - the setup query is more complex, but not too much. I fixed the
size calculation, when nested partitions tables are visible - it calculate
partitions only from level1 group. Then the displayed size is same as total
size

\dPn seems to work fine, but I don't quite understand why \dPn+ should
show the sizes only for nested partitions of level. Consider the
following example outputs:

create table p (a int, b char) partition by list (a);
create table p_1 partition of p for values in (1) partition by list (b);
create table p_1_a partition of p_1 for values in ('a');
create table p_1_bc partition of p_1 for values in ('b', 'c') partition by
list (b);
create table p_1_b partition of p_1_bc for values in ('b');
create table p_1_c partition of p_1_bc for values in ('c');
create table p_2 partition of p for values in (2);
insert into p values (1, 'a');
insert into p values (1, 'b');
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

-- size of 'p' shown as 8KB, whereas it's actually 24KB as per above
-- size of 'p_1' shown as 8KB, whereas it's actually 16KB
\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 8192 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Also, if the root partitioned table doesn't have a directly attached leaf
partition, it's size is shown as 0 bytes, whereas I think it should
consider the sizes of its other nested partitions.

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 0 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

If I remove the following two statements from the patched code:

+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n         WHERE d.level = 1");
+            /*
+             * Assign size just for directly assigned tables, when nested
+             * partitions are visible
+             */
+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n     WHERE ppt.isleaf AND
ppt.level = 1");

I get the following output, which I find more intuitive:

create table p_2 partition of p for values in (2);
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 24 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 16 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Thoughts?

Meanwhile, some comments on the patch:

+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too.

Maybe, say "non-root partitioned tables" instead of "nested partition
tables". Same comment also applies for the same text in the paragraphs
for \dPni and \dPnt too.

+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi

Maybe mention the 'n' modifier here?

+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool
show_tables, bool show_nested_partitions)
+{

I'm not sure if psql's source code formatting guidelines are different
from the backend code, but putting all the arguments on the same line
causes the line grow well over 78 characters. Can you wrap maybe?

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation" */
+            psql_error("Did not find any partitioned %s.\n",
+                      object_name);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                       objects_name,
+                       pattern);

You're using the variable 'pattern' in the block where it's supposed to be
NULL. Maybe it should be:

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation" */
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                      object_name, pattern);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s.\n",
+                       objects_name);

Thanks,
Amit

#55Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#54)
Re: ToDo: show size of partitioned table

čt 7. 2. 2019 v 9:51 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Hi Pavel,

Thanks for sending the updated patch.

On 2018/12/19 15:38, Pavel Stehule wrote:

út 18. 12. 2018 v 8:49 odesílatel Amit Langote <

On 2018/12/17 17:48, Pavel Stehule wrote:

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

Too much complication maybe?

I wrote it - the setup query is more complex, but not too much. I fixed

the

size calculation, when nested partitions tables are visible - it

calculate

partitions only from level1 group. Then the displayed size is same as

total

size

\dPn seems to work fine, but I don't quite understand why \dPn+ should
show the sizes only for nested partitions of level. Consider the
following example outputs:

Show nested objects in rectangular output is a problem. I prefer a design
where any times the sum of displayed sizes is same like total size.

So if I have partitions on level1 of size 16KB, and on level 2 8KB, then I
would to display 16 and 8, and not 24 and 8. If I remember, this rule is
modified, when filter is used.

Maybe we can introduce more columns where totals from leaves can be
calculated.

Regards

Pavel

Show quoted text

create table p (a int, b char) partition by list (a);
create table p_1 partition of p for values in (1) partition by list (b);
create table p_1_a partition of p_1 for values in ('a');
create table p_1_bc partition of p_1 for values in ('b', 'c') partition by
list (b);
create table p_1_b partition of p_1_bc for values in ('b');
create table p_1_c partition of p_1_bc for values in ('c');
create table p_2 partition of p for values in (2);
insert into p values (1, 'a');
insert into p values (1, 'b');
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

-- size of 'p' shown as 8KB, whereas it's actually 24KB as per above
-- size of 'p_1' shown as 8KB, whereas it's actually 16KB
\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 8192 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Also, if the root partitioned table doesn't have a directly attached leaf
partition, it's size is shown as 0 bytes, whereas I think it should
consider the sizes of its other nested partitions.

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 0 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

If I remove the following two statements from the patched code:

+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n         WHERE d.level = 1");
+            /*
+             * Assign size just for directly assigned tables, when nested
+             * partitions are visible
+             */
+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n     WHERE ppt.isleaf AND
ppt.level = 1");

I get the following output, which I find more intuitive:

create table p_2 partition of p for values in (2);
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 24 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 16 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Thoughts?

Meanwhile, some comments on the patch:

+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too.

Maybe, say "non-root partitioned tables" instead of "nested partition
tables". Same comment also applies for the same text in the paragraphs
for \dPni and \dPnt too.

+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi

Maybe mention the 'n' modifier here?

+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool
show_tables, bool show_nested_partitions)
+{

I'm not sure if psql's source code formatting guidelines are different
from the backend code, but putting all the arguments on the same line
causes the line grow well over 78 characters. Can you wrap maybe?

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation"
*/
+            psql_error("Did not find any partitioned %s.\n",
+                      object_name);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                       objects_name,
+                       pattern);

You're using the variable 'pattern' in the block where it's supposed to be
NULL. Maybe it should be:

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation"
*/
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                      object_name, pattern);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s.\n",
+                       objects_name);

Thanks,
Amit

#56Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#55)
Re: ToDo: show size of partitioned table

Hi,

On 2019/02/07 18:08, Pavel Stehule wrote:

čt 7. 2. 2019 v 9:51 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>

\dPn seems to work fine, but I don't quite understand why \dPn+ should
show the sizes only for nested partitions of level. Consider the

(correcting words of my previous email: ... of level 1.)

Show nested objects in rectangular output is a problem. I prefer a design
where any times the sum of displayed sizes is same like total size.

So if I have partitions on level1 of size 16KB, and on level 2 8KB, then I
would to display 16 and 8, and not 24 and 8. If I remember, this rule is
modified, when filter is used.

Just to recap, the originally proposed feature is to show the size of a
partitioned table by summing the sizes of *all* of its (actually leaf)
partitions, which \dP[t|i]+ gives us. As you mentioned, a limitation of
the feature as initially proposed is that it only shows partitioned tables
that are roots of their respective partition trees. That is, there is no
way to see the sizes of the intermediate partitioned tables using any of
psql's \d commands. So, you introduced the "n" modifier, whereby
\dP[t|i]n+ now shows *also* the intermediate partitioned tables with their
sizes. But it only considers the directly attached partitions of each
partitioned table that's shown. So, only those partitioned tables that
have leaf partitions directly attached them are shown with non-0 size (if
leaf partitions are non-empty) and others with size 0 (root partitioned
tables in most cases where nested partitioned tables are involved). But I
think that means the "n" modifier is changing the behavior of the base
command (\dP+) which is meant to show the total size of *all* partitions
under a given partitioned table. Maybe, the "n" modifier should only
result in including the nested/intermediate partitioned tables and nothing
more than that.

I see your point that all these tables are appearing in the display as one
flat list and so the sizes of same leaf partitions may be multiply
counted, but it's not totally a flat representation given that you have
added "Parent name" column. We could document that the size of a nested
partitioned table shown in the display is also counted in the size of its
parent partitioned table. That I think may be easier to understand than
that the size of each partitioned table shown in the display only
considers the sizes of leaf partitions that are directly attached to it.

Thoughts? Any more opinions on this?

Thanks,
Amit

#57Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#56)
Re: ToDo: show size of partitioned table

čt 7. 2. 2019 v 11:25 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Hi,

On 2019/02/07 18:08, Pavel Stehule wrote:

čt 7. 2. 2019 v 9:51 odesílatel Amit Langote <

Langote_Amit_f8@lab.ntt.co.jp>

\dPn seems to work fine, but I don't quite understand why \dPn+ should
show the sizes only for nested partitions of level. Consider the

(correcting words of my previous email: ... of level 1.)

Show nested objects in rectangular output is a problem. I prefer a design
where any times the sum of displayed sizes is same like total size.

So if I have partitions on level1 of size 16KB, and on level 2 8KB, then

I

would to display 16 and 8, and not 24 and 8. If I remember, this rule is
modified, when filter is used.

Just to recap, the originally proposed feature is to show the size of a
partitioned table by summing the sizes of *all* of its (actually leaf)
partitions, which \dP[t|i]+ gives us. As you mentioned, a limitation of
the feature as initially proposed is that it only shows partitioned tables
that are roots of their respective partition trees. That is, there is no
way to see the sizes of the intermediate partitioned tables using any of
psql's \d commands. So, you introduced the "n" modifier, whereby
\dP[t|i]n+ now shows *also* the intermediate partitioned tables with their
sizes. But it only considers the directly attached partitions of each
partitioned table that's shown. So, only those partitioned tables that
have leaf partitions directly attached them are shown with non-0 size (if
leaf partitions are non-empty) and others with size 0 (root partitioned
tables in most cases where nested partitioned tables are involved). But I
think that means the "n" modifier is changing the behavior of the base
command (\dP+) which is meant to show the total size of *all* partitions
under a given partitioned table. Maybe, the "n" modifier should only
result in including the nested/intermediate partitioned tables and nothing
more than that.

It was a Michael's request to see all hierarchy, and I think so it has some
benefits

I see your point that all these tables are appearing in the display as one
flat list and so the sizes of same leaf partitions may be multiply
counted, but it's not totally a flat representation given that you have
added "Parent name" column. We could document that the size of a nested
partitioned table shown in the display is also counted in the size of its
parent partitioned table. That I think may be easier to understand than
that the size of each partitioned table shown in the display only
considers the sizes of leaf partitions that are directly attached to it.

Personally I don't agree - a) who read a documentation, b) it is really
violation of some relation principles. It is clean, if we have only one
table, but if we see a report with more tables, than multiple size
calculation can be messy.

It is not problem if you have clean schema like P1, P2, tables (when tree
is balanced). But when some tables can be assigned to P1 and some to P2
(tree is not balanced) then it is not clean what is size of directly
attached tables and what is size of subpartitions. So it is better don't
sum apples and oranges.

\dPn shows all subroots and related minimal size. I think so this is very
clear definition.

Your example

postgres=# \dPtn+
List of partitioned tables
┌────────┬────────┬───────┬─────────────┬────────────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Size │ Description │
╞════════╪════════╪═══════╪═════════════╪════════════╪═════════════╡
│ public │ p │ pavel │ │ 8192 bytes │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ │
└────────┴────────┴───────┴─────────────┴────────────┴─────────────┘
(3 rows)

I hope so the interpretation is clean .. there are three partitioned tables
(two are subpartitioned tables). Any partitioned table has assigned 8KB of
data.

We can introduce new column "size with sub partitions" where these numbers
can be counted together. But for term "size" I expect valid rule S1+S2+..
SN = total size.

It is acceptable for you?

Show quoted text

Thoughts? Any more opinions on this?

Thanks,
Amit

#58Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#57)
Re: ToDo: show size of partitioned table

On 2019-Feb-07, Pavel Stehule wrote:

Your example

postgres=# \dPtn+
List of partitioned tables
┌────────┬────────┬───────┬─────────────┬────────────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Size │ Description │
╞════════╪════════╪═══════╪═════════════╪════════════╪═════════════╡
│ public │ p │ pavel │ │ 8192 bytes │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ │
└────────┴────────┴───────┴─────────────┴────────────┴─────────────┘
(3 rows)

I hope so the interpretation is clean .. there are three partitioned tables
(two are subpartitioned tables). Any partitioned table has assigned 8KB of
data.

We can introduce new column "size with sub partitions" where these numbers
can be counted together. But for term "size" I expect valid rule S1+S2+..
SN = total size.

Right now, a partitioned table has no size of its own; its only storage
is in its children. But that might change in the future, for example if
we implement global indexes. Then it will be useful to show these sizes
separately.

I suggest that we should have one column for the aggregated size of its
children, and another column for the "local" size. So currently the
local size would always be zero for partitioned tables. The other
column (maybe "Children Size") would be the sum of the sizes of all its
partitions.

So I think this should be:

List of partitioned tables
┌────────┬────────┬───────┬─────────────┬────────────┬───────────────┬────────────
│ Schema │ Name │ Owner │ Parent name │ Size │ Children Size │ Description
╞════════╪════════╪═══════╪═════════════╪════════════╪═══════════════╡
│ public │ p │ pavel │ │ │ 8192 bytes │
│ public │ p_1 │ pavel │ p │ │ 8192 bytes │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ │
└────────┴────────┴───────┴─────────────┴────────────┴───────────────┴─────────────

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#59Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#58)
Re: ToDo: show size of partitioned table

čt 7. 2. 2019 v 16:03 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Feb-07, Pavel Stehule wrote:

Your example

postgres=# \dPtn+
List of partitioned tables
┌────────┬────────┬───────┬─────────────┬────────────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Size │ Description │
╞════════╪════════╪═══════╪═════════════╪════════════╪═════════════╡
│ public │ p │ pavel │ │ 8192 bytes │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ │
└────────┴────────┴───────┴─────────────┴────────────┴─────────────┘
(3 rows)

I hope so the interpretation is clean .. there are three partitioned

tables

(two are subpartitioned tables). Any partitioned table has assigned 8KB

of

data.

We can introduce new column "size with sub partitions" where these

numbers

can be counted together. But for term "size" I expect valid rule S1+S2+..
SN = total size.

Right now, a partitioned table has no size of its own; its only storage
is in its children. But that might change in the future, for example if
we implement global indexes. Then it will be useful to show these sizes
separately.

I suggest that we should have one column for the aggregated size of its
children, and another column for the "local" size. So currently the
local size would always be zero for partitioned tables. The other
column (maybe "Children Size") would be the sum of the sizes of all its
partitions.

So I think this should be:

So this is third proposals :-)

Can I recapitulate it?

1. My proposal - the "size" = sum(partitions on level1), "size with nested
partitioned tables" = sum(partitions on all levels)
2. Amit's proposal - the "size" = sum(partions on all levels)
3. Alvaro's proposal - the "size" = empty now, "children size" =
sum(partitions on level1)

Every proposal is valid, and has some sense and display some information
from some perspective.

So first we should not to use one word term "size" in this context, because
there is any agreement.

Can we find some terminology for two *sizes*?

1. size of all partitions with nesting level = 1
2. size of all partitions without nesting level check.

maybe there can be third variant - size of all partitions with nesting
level > 1

I am not sure so "children size" is good term because this structure is
tree, and the deep is not specified.

Maybe "Immediate partitions size" for @1, "total partitions size" for @2,
and "indirect partitions size" for @3.

Then we can display @1, @2 or @1 or @3.

I hope so I mentioned all possible variants.

Comments, notes?

Pavel

Show quoted text

List of partitioned tables

┌────────┬────────┬───────┬─────────────┬────────────┬───────────────┬────────────
│ Schema │ Name │ Owner │ Parent name │ Size │ Children Size │
Description
╞════════╪════════╪═══════╪═════════════╪════════════╪═══════════════╡
│ public │ p │ pavel │ │ │ 8192 bytes │
│ public │ p_1 │ pavel │ p │ │ 8192 bytes │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ │

└────────┴────────┴───────┴─────────────┴────────────┴───────────────┴─────────────

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#60Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#54)
1 attachment(s)
Re: ToDo: show size of partitioned table

čt 7. 2. 2019 v 9:51 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Hi Pavel,

Thanks for sending the updated patch.

On 2018/12/19 15:38, Pavel Stehule wrote:

út 18. 12. 2018 v 8:49 odesílatel Amit Langote <

On 2018/12/17 17:48, Pavel Stehule wrote:

I can imagine new additional flag - line "n" nested - and then we can
display nested partitioned tables with parent table info. Some like

\dPt - show only root partition tables
\dPnt or \dPtn - show root and nested partitioned tables

Too much complication maybe?

I wrote it - the setup query is more complex, but not too much. I fixed

the

size calculation, when nested partitions tables are visible - it

calculate

partitions only from level1 group. Then the displayed size is same as

total

size

\dPn seems to work fine, but I don't quite understand why \dPn+ should
show the sizes only for nested partitions of level. Consider the
following example outputs:

create table p (a int, b char) partition by list (a);
create table p_1 partition of p for values in (1) partition by list (b);
create table p_1_a partition of p_1 for values in ('a');
create table p_1_bc partition of p_1 for values in ('b', 'c') partition by
list (b);
create table p_1_b partition of p_1_bc for values in ('b');
create table p_1_c partition of p_1_bc for values in ('c');
create table p_2 partition of p for values in (2);
insert into p values (1, 'a');
insert into p values (1, 'b');
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

-- size of 'p' shown as 8KB, whereas it's actually 24KB as per above
-- size of 'p_1' shown as 8KB, whereas it's actually 16KB
\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 8192 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Also, if the root partitioned table doesn't have a directly attached leaf
partition, it's size is shown as 0 bytes, whereas I think it should
consider the sizes of its other nested partitions.

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 0 bytes │
public │ p_1 │ amit │ p │ 8192 bytes │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

If I remove the following two statements from the patched code:

+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n         WHERE d.level = 1");
+            /*
+             * Assign size just for directly assigned tables, when nested
+             * partitions are visible
+             */
+            if (show_nested_partitions)
+                appendPQExpBuffer(&buf, "\n     WHERE ppt.isleaf AND
ppt.level = 1");

I get the following output, which I find more intuitive:

create table p_2 partition of p for values in (2);
insert into p values (2);

\dP+
List of partitioned relations
Schema │ Name │ Owner │ Size │ Description
────────┼──────┼───────┼───────┼─────────────
public │ p │ amit │ 24 kB │
(1 row)

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 24 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

drop table p_2;

\dPn+
List of partitioned relations
Schema │ Name │ Owner │ Parent name │ Size │ Description
────────┼────────┼───────┼─────────────┼────────────┼─────────────
public │ p │ amit │ │ 16 kB │
public │ p_1 │ amit │ p │ 16 kB │
public │ p_1_bc │ amit │ p_1 │ 8192 bytes │
(3 rows)

Thoughts?

I renamed originally calculated column "size" to "direct partitions size"
.. see Alvaro's comment. Then I introduced new column "total partitions
size" that is calculated like you propose.

Now the result of dPn+ looks like

List of partitioned relations
┌────────┬────────┬───────┬─────────────┬────────────────────────┬───────────────────────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Direct partitions size │ Total
partitions size │ Description │
╞════════╪════════╪═══════╪═════════════╪════════════════════════╪═══════════════════════╪═════════════╡
│ public │ p │ pavel │ │ 8192 bytes │ 24
kB │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ 16
kB │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ 8192
bytes │ │
└────────┴────────┴───────┴─────────────┴────────────────────────┴───────────────────────┴─────────────┘
(3 rows)

Meanwhile, some comments on the patch:

+         If modifier <literal>n</literal> is used, then nested partition
+         tables are displayed too.

Maybe, say "non-root partitioned tables" instead of "nested partition
tables". Same comment also applies for the same text in the paragraphs
for \dPni and \dPnt too.

fixed

+/*
+ * listPartitions()
+ *
+ * handler for \dP, \dPt and \dPi

Maybe mention the 'n' modifier here?

fixed

+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes, bool
show_tables, bool show_nested_partitions)
+{

fixed

I'm not sure if psql's source code formatting guidelines are different
from the backend code, but putting all the arguments on the same line
causes the line grow well over 78 characters. Can you wrap maybe?

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation"
*/
+            psql_error("Did not find any partitioned %s.\n",
+                      object_name);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                       objects_name,
+                       pattern);

You're using the variable 'pattern' in the block where it's supposed to be
NULL. Maybe it should be:

+        if (pattern)
+            /* translator: object_name is "index", "table" or "relation"
*/
+            psql_error("Did not find any partitioned %s named \"%s\".\n",
+                      object_name, pattern);
+        else
+            /* translator: objects_name is "indexes", "tables" or
"relations" */
+            psql_error("Did not find any partitioned %s.\n",
+                       objects_name);

fixed

attached updated patch

Regards

Pavel

Show quoted text

Thanks,
Amit

Attachments:

psql-dP-9.patchtext/x-patch; charset=US-ASCII; name=psql-dP-9.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index d7539ae743..76a6ceb0dd 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,71 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose relation name or schema name matches
+        the pattern are listed. If the form <literal>\dP+</literal>
+        is used, the sum of sizes of related partitions (including the
+        table and indexes, if any) and associated description
+        are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then non-root partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose index name or schema name matches
+        the pattern are listed. If the form <literal>\dPi+</literal>
+        is used, the sum of sizes of related indexes and associated
+        description are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then non-root partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only
+        entries whose table name or schema name matches
+        the pattern are listed. If the form <literal>\dPt+</literal>
+        is used, the sum of sizes of related tables and associated
+        description are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> is used, then non-root partition
+         tables are displayed too. The size is calculated just for directly
+         assigned partitions (not for nested partitions).
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ab259c473a..42ffbbe630 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,36 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					bool show_nested_partitions = strchr(cmd, 'n') ? true : false;
+
+					switch (cmd[2])
+					{
+						case 'i':
+							/* show indexes only */
+							success = listPartitions(pattern, show_verbose, true, false, show_nested_partitions);
+							break;
+						case 't':
+							/* show tables only */
+							success = listPartitions(pattern, show_verbose, false, true, show_nested_partitions);
+							break;
+						case '+':
+						case '\0':
+						case 'n':
+							/*
+							 * show relations - when there are not pattern, then it shows
+							 * tables with total relation size, else where it shows tables
+							 * and indexes.
+							 */
+							success = listPartitions(pattern, show_verbose, false, false, show_nested_partitions);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4da6719ce7..e69fd0d4c0 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,248 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP[t|i][n]
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes,
+			   bool show_tables, bool show_nested_partitions)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			object_name = gettext_noop("relation");
+			objects_name = gettext_noop("relations");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			object_name = gettext_noop("relation or index");
+			objects_name = gettext_noop("relations or indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+		translate_columns[4] = true;
+	}
+
+	if (show_nested_partitions)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (show_nested_partitions)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Direct partitions size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total partitions size"));
+		}
+		else
+			/* Both previous sizes are same in this case. Show only one */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Partitions size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (show_nested_partitions)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.%s(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s",
+							  size_function, size_function);
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.%s(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.%s(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s",
+							  size_function, size_function);
+		}
+	}
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !show_nested_partitions ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "indexes", "tables" or "relations" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   objects_name,
+					   pattern);
+		else
+			/* translator: object_name is "index", "table" or "relation" */
+			psql_error("Did not find any partitioned %s.\n",
+					  object_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" or "relations" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f38..4fc1cacbee 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP[n], \dPi[n], \dPt[n] */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested_partitions);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 7c6fa2c590..7137452842 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,7 +168,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -250,6 +250,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7b7a88fda3..928ee9a5cb 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1381,7 +1399,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3503,6 +3521,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 775b127121..52bcb3f918 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4539,3 +4539,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                           List of partitioned relations or indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                     List of partitioned relations or indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+         List of partitioned relations
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                List of partitioned relations
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPn testpart.*
+                                List of partitioned relations or indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 1bb2a6e16d..540a416a7b 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1016,3 +1016,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#61Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#60)
1 attachment(s)
Re: ToDo: show size of partitioned table

Hi Pavel,

Thanks for updating the patch.

On 2019/02/08 17:26, Pavel Stehule wrote:

I renamed originally calculated column "size" to "direct partitions size"
.. see Alvaro's comment. Then I introduced new column "total partitions
size" that is calculated like you propose.

Now the result of dPn+ looks like

List of partitioned relations
┌────────┬────────┬───────┬─────────────┬────────────────────────┬───────────────────────┬─────────────┐
│ Schema │ Name │ Owner │ Parent name │ Direct partitions size │ Total
partitions size │ Description │
╞════════╪════════╪═══════╪═════════════╪════════════════════════╪═══════════════════════╪═════════════╡
│ public │ p │ pavel │ │ 8192 bytes │ 24
kB │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ 16
kB │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ 8192
bytes │ │
└────────┴────────┴───────┴─────────────┴────────────────────────┴───────────────────────┴─────────────┘
(3 rows)

OK, so for each listed partitioned table (root and nested), this shows the
total size of the directly attached leaf partitions *and* the total size
of all partitions in its (sub-) tree.

By the way, what I think Alvaro meant by "local size" is not what the
"direct partition size" above shows. I think "local size" means the size
of the storage assigned to the table itself, not to partitions attached to
it, which are distinct relations. We don't implement that concept in
Postgres today, but may in the future. I'm not sure if we'll add a
another column to show "local size" in the future when we do implement
that concept or if Alvaro meant that there should only be "local size"
(not "direct partition size") which will always show 0 for now and "total
partition size" columns.

Anyway, I have a few more suggestions to improve the patch, but instead of
sending the minute-level changes in the email, I've gone ahead and made
those changes myself. I've attached a delta patch that applies on top of
your v9 patch. Summary of the changes I made is as follows:

* Documentation rewording here and there (also mentioned the "direct
partitions size" and "total partitions size" division in the \dPn output
per the latest patch)

* Wrapped some lines in code so that they don't look too wide

* Renamed show_nested_partitions to show_nested

* Changed "Partitioned relations" in the output headers to say
"Partitioned tables" where appropriate

* Fixed quiet mode output to use correct word between object_name vs
objects_name

Please merge these changes if you think they are reasonable.

Thanks,
Amit

Attachments:

v9-delta-amit.patchtext/plain; charset=UTF-8; name=v9-delta-amit.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 76a6ceb0dd..9fb632b0bd 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1664,19 +1664,23 @@ testdb=&gt;
         <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
-        Lists partitioned relations. If <replaceable
-        class="parameter">pattern</replaceable> is specified, only
-        entries whose relation name or schema name matches
-        the pattern are listed. If the form <literal>\dP+</literal>
-        is used, the sum of sizes of related partitions (including the
-        table and indexes, if any) and associated description
-        are also displayed.
+        Lists partitioned relations.  If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed.  By default, only
+        partitioned tables are listed; supply a pattern to also include
+        partitioned indexes.  If the form <literal>\dP+</literal>
+        is used, the sum of sizes of table's partitions (including their
+        indexes) and associated description are also displayed.
         </para>
 
         <para>
-         If modifier <literal>n</literal> is used, then non-root partition
-         tables are displayed too. The size is calculated just for directly
-         assigned partitions (not for nested partitions).
+         If modifier <literal>n</literal> (which stands for
+         <quote>nested</quote>) is used, then non-root partitioned tables are
+         displayed too.  The displayed size is divided into two columns in
+         this case: one that shows the total size of only the directly
+         attached leaf partitions and another that shows total size of all
+         partitions, also considering other sub-partitioned partitions, for
+         each partitioned tables that's displayed.
         </para>
         </listitem>
       </varlistentry>
@@ -1687,17 +1691,15 @@ testdb=&gt;
         <listitem>
         <para>
         Lists partitioned indexes. If <replaceable
-        class="parameter">pattern</replaceable> is specified, only
-        entries whose index name or schema name matches
-        the pattern are listed. If the form <literal>\dPi+</literal>
-        is used, the sum of sizes of related indexes and associated
-        description are also displayed.
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPi+</literal> is used, the sum of sizes of index's
+        partitions and associated description are also displayed.
         </para>
 
         <para>
-         If modifier <literal>n</literal> is used, then non-root partition
-         tables are displayed too. The size is calculated just for directly
-         assigned partitions (not for nested partitions).
+         If the modifier <literal>n</literal> is used, non-root partitioned
+         indexes are displayed too.
         </para>
         </listitem>
       </varlistentry>
@@ -1708,17 +1710,15 @@ testdb=&gt;
         <listitem>
         <para>
         Lists partitioned tables. If <replaceable
-        class="parameter">pattern</replaceable> is specified, only
-        entries whose table name or schema name matches
-        the pattern are listed. If the form <literal>\dPt+</literal>
-        is used, the sum of sizes of related tables and associated
-        description are also displayed.
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPt+</literal> is used, the sum of sizes of table's
+        partitions and associated description are also displayed.
         </para>
 
         <para>
-         If modifier <literal>n</literal> is used, then non-root partition
-         tables are displayed too. The size is calculated just for directly
-         assigned partitions (not for nested partitions).
+         If the modifier <literal>n</literal> is used, non-root partitioned
+         tables are displayed too.
         </para>
         </listitem>
       </varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 42ffbbe630..e3d4752857 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -784,27 +784,33 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				break;
 			case 'P':
 				{
-					bool show_nested_partitions = strchr(cmd, 'n') ? true : false;
+					bool show_nested = strchr(cmd, 'n') ? true : false;
 
 					switch (cmd[2])
 					{
 						case 'i':
 							/* show indexes only */
-							success = listPartitions(pattern, show_verbose, true, false, show_nested_partitions);
+							success = listPartitions(pattern, show_verbose,
+													 true, false,
+													 show_nested);
 							break;
 						case 't':
 							/* show tables only */
-							success = listPartitions(pattern, show_verbose, false, true, show_nested_partitions);
+							success = listPartitions(pattern, show_verbose,
+													 false, true,
+													 show_nested);
 							break;
 						case '+':
 						case '\0':
 						case 'n':
 							/*
-							 * show relations - when there are not pattern, then it shows
-							 * tables with total relation size, else where it shows tables
-							 * and indexes.
+							 * Show only tables if there is no pattern.  Also
+							 * show indexes if pattern is specified.  Show
+							 * total size if verbose output is specified.
 							 */
-							success = listPartitions(pattern, show_verbose, false, false, show_nested_partitions);
+							success = listPartitions(pattern, show_verbose,
+													 false, false,
+													 show_nested);
 							break;
 						default:
 							status = PSQL_CMD_UNKNOWN;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index e69fd0d4c0..2b8628f2ff 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3666,7 +3666,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
  */
 bool
 listPartitions(const char *pattern, bool verbose, bool show_indexes,
-			   bool show_tables, bool show_nested_partitions)
+			   bool show_tables, bool show_nested)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -3714,16 +3714,16 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 		{
 			size_function = "pg_total_relation_size";
 			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
-			object_name = gettext_noop("relation");
-			objects_name = gettext_noop("relations");
+			object_name = gettext_noop("table");
+			objects_name = gettext_noop("tables");
 		}
 		else
 		{
 			size_function = "pg_table_size";
 			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
 							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
-			object_name = gettext_noop("relation or index");
-			objects_name = gettext_noop("relations or indexes");
+			object_name = gettext_noop("table or index");
+			objects_name = gettext_noop("tables or indexes");
 			mixed_output = true;
 		}
 	}
@@ -3752,7 +3752,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 		translate_columns[4] = true;
 	}
 
-	if (show_nested_partitions)
+	if (show_nested)
 		appendPQExpBuffer(&buf,
 						  ",\n  c3.relname as \"%s\"",
 						  gettext_noop("Parent name"));
@@ -3764,7 +3764,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 
 	if (verbose)
 	{
-		if (show_nested_partitions)
+		if (show_nested)
 		{
 			appendPQExpBuffer(&buf,
 							  ",\n  s.dps as \"%s\"",
@@ -3774,7 +3774,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 							  gettext_noop("Total partitions size"));
 		}
 		else
-			/* Both previous sizes are same in this case. Show only one */
+			/* Sizes of all partitions are considered in this case. */
 			appendPQExpBuffer(&buf,
 							  ",\n  s.tps as \"%s\"",
 							  gettext_noop("Partitions size"));
@@ -3793,7 +3793,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
 							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
 
-	if (show_nested_partitions)
+	if (show_nested)
 		appendPQExpBufferStr(&buf,
 							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
 							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
@@ -3835,7 +3835,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 	}
 
 	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
-	appendPQExpBufferStr(&buf, !show_nested_partitions ? " AND NOT c.relispartition\n" : "\n");
+	appendPQExpBufferStr(&buf, !show_nested ? " AND NOT c.relispartition\n" : "\n");
 
 	if (!pattern)
 		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -3869,14 +3869,14 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 	if (PQntuples(res) == 0 && !pset.quiet)
 	{
 		if (pattern)
-			/* translator: objects_name is "indexes", "tables" or "relations" */
+			/* translator: objects_name is "index", "table" */
 			psql_error("Did not find any partitioned %s named \"%s\".\n",
-					   objects_name,
+					   object_name,
 					   pattern);
 		else
-			/* translator: object_name is "index", "table" or "relation" */
+			/* translator: object_name is "indexes", "tables" */
 			psql_error("Did not find any partitioned %s.\n",
-					  object_name);
+					  objects_name);
 	}
 	else
 	{
@@ -3884,7 +3884,7 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 
 		initPQExpBuffer(&title);
 
-		/* translator: objects_name is "indexes", "tables" or "relations" */
+		/* translator: objects_name is "indexes", "tables" */
 		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
 
 		myopt.nullPrint = NULL;
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4fc1cacbee..e3c6dda021 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -64,7 +64,7 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
 /* \dP[n], \dPi[n], \dPt[n] */
-extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested_partitions);
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested);
 
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 52bcb3f918..2167b14fe5 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4555,7 +4555,7 @@ create index testpart_apple_index on testpart_apple(logdate);
 create index testpart_orange_index on testpart_orange(logdate);
 -- only partition related object should be displayed
 \dP test*apple*
-                           List of partitioned relations or indexes
+                            List of partitioned tables or indexes
   Schema  |         Name         |         Owner         |       Type        |    On table    
 ----------+----------------------+-----------------------+-------------------+----------------
  testpart | testpart_apple       | testrole_partitioning | partitioned table | 
@@ -4612,7 +4612,7 @@ insert into parent_tab values (generate_series(30,39));
 (1 row)
 
 \dP testpart.*
-                     List of partitioned relations or indexes
+                      List of partitioned tables or indexes
   Schema  |     Name     |         Owner         |       Type        |  On table  
 ----------+--------------+-----------------------+-------------------+------------
  testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
@@ -4620,7 +4620,7 @@ insert into parent_tab values (generate_series(30,39));
 (2 rows)
 
 \dP
-         List of partitioned relations
+          List of partitioned tables
   Schema  |    Name    |         Owner         
 ----------+------------+-----------------------
  testpart | parent_tab | testrole_partitioning
@@ -4643,7 +4643,7 @@ insert into parent_tab values (generate_series(30,39));
 (2 rows)
 
 \dPn
-                List of partitioned relations
+                  List of partitioned tables
   Schema  |    Name     |         Owner         | Parent name 
 ----------+-------------+-----------------------+-------------
  testpart | child_30_40 | testrole_partitioning | parent_tab
@@ -4651,7 +4651,7 @@ insert into parent_tab values (generate_series(30,39));
 (2 rows)
 
 \dPn testpart.*
-                                List of partitioned relations or indexes
+                                 List of partitioned tables or indexes
   Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
 ----------+--------------------+-----------------------+-------------------+--------------+-------------
  testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
#62Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#61)
1 attachment(s)
Re: ToDo: show size of partitioned table

pá 15. 2. 2019 v 7:50 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Hi Pavel,

Thanks for updating the patch.

On 2019/02/08 17:26, Pavel Stehule wrote:

I renamed originally calculated column "size" to "direct partitions size"
.. see Alvaro's comment. Then I introduced new column "total partitions
size" that is calculated like you propose.

Now the result of dPn+ looks like

List of partitioned relations

┌────────┬────────┬───────┬─────────────┬────────────────────────┬───────────────────────┬─────────────┐

│ Schema │ Name │ Owner │ Parent name │ Direct partitions size │ Total
partitions size │ Description │

╞════════╪════════╪═══════╪═════════════╪════════════════════════╪═══════════════════════╪═════════════╡

│ public │ p │ pavel │ │ 8192 bytes │ 24
kB │ │
│ public │ p_1 │ pavel │ p │ 8192 bytes │ 16
kB │ │
│ public │ p_1_bc │ pavel │ p_1 │ 8192 bytes │ 8192
bytes │ │

└────────┴────────┴───────┴─────────────┴────────────────────────┴───────────────────────┴─────────────┘

(3 rows)

OK, so for each listed partitioned table (root and nested), this shows the
total size of the directly attached leaf partitions *and* the total size
of all partitions in its (sub-) tree.

By the way, what I think Alvaro meant by "local size" is not what the
"direct partition size" above shows. I think "local size" means the size
of the storage assigned to the table itself, not to partitions attached to
it, which are distinct relations. We don't implement that concept in
Postgres today, but may in the future. I'm not sure if we'll add a
another column to show "local size" in the future when we do implement
that concept or if Alvaro meant that there should only be "local size"
(not "direct partition size") which will always show 0 for now and "total
partition size" columns.

We can do it in future. Now, I don't think so is good to show 0 always. The
psql reports (like this) can be enhanced or changed in future without
problems, so we don't need to design all now.

Anyway, I have a few more suggestions to improve the patch, but instead of
sending the minute-level changes in the email, I've gone ahead and made
those changes myself. I've attached a delta patch that applies on top of
your v9 patch. Summary of the changes I made is as follows:

* Documentation rewording here and there (also mentioned the "direct
partitions size" and "total partitions size" division in the \dPn output
per the latest patch)

* Wrapped some lines in code so that they don't look too wide

* Renamed show_nested_partitions to show_nested

* Changed "Partitioned relations" in the output headers to say
"Partitioned tables" where appropriate

* Fixed quiet mode output to use correct word between object_name vs
objects_name

Please merge these changes if you think they are reasonable.

I like your changes. I merged all - updated patch is attached

Thank you very much

Regards

Pavel

Show quoted text

Thanks,
Amit

Attachments:

psql-dP-10.patchtext/x-patch; charset=US-ASCII; name=psql-dP-10.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index d7539ae743..9fb632b0bd 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,71 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations.  If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed.  By default, only
+        partitioned tables are listed; supply a pattern to also include
+        partitioned indexes.  If the form <literal>\dP+</literal>
+        is used, the sum of sizes of table's partitions (including their
+        indexes) and associated description are also displayed.
+        </para>
+
+        <para>
+         If modifier <literal>n</literal> (which stands for
+         <quote>nested</quote>) is used, then non-root partitioned tables are
+         displayed too.  The displayed size is divided into two columns in
+         this case: one that shows the total size of only the directly
+         attached leaf partitions and another that shows total size of all
+         partitions, also considering other sub-partitioned partitions, for
+         each partitioned tables that's displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPi+</literal> is used, the sum of sizes of index's
+        partitions and associated description are also displayed.
+        </para>
+
+        <para>
+         If the modifier <literal>n</literal> is used, non-root partitioned
+         indexes are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPt+</literal> is used, the sum of sizes of table's
+        partitions and associated description are also displayed.
+        </para>
+
+        <para>
+         If the modifier <literal>n</literal> is used, non-root partitioned
+         tables are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ab259c473a..e3d4752857 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,42 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					bool show_nested = strchr(cmd, 'n') ? true : false;
+
+					switch (cmd[2])
+					{
+						case 'i':
+							/* show indexes only */
+							success = listPartitions(pattern, show_verbose,
+													 true, false,
+													 show_nested);
+							break;
+						case 't':
+							/* show tables only */
+							success = listPartitions(pattern, show_verbose,
+													 false, true,
+													 show_nested);
+							break;
+						case '+':
+						case '\0':
+						case 'n':
+							/*
+							 * Show only tables if there is no pattern.  Also
+							 * show indexes if pattern is specified.  Show
+							 * total size if verbose output is specified.
+							 */
+							success = listPartitions(pattern, show_verbose,
+													 false, false,
+													 show_nested);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4da6719ce7..2b8628f2ff 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3659,6 +3659,248 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP[t|i][n]
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes,
+			   bool show_tables, bool show_nested)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			object_name = gettext_noop("table");
+			objects_name = gettext_noop("tables");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			object_name = gettext_noop("table or index");
+			objects_name = gettext_noop("tables or indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+		translate_columns[4] = true;
+	}
+
+	if (show_nested)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (show_nested)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Direct partitions size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total partitions size"));
+		}
+		else
+			/* Sizes of all partitions are considered in this case. */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Partitions size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (show_nested)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.%s(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s",
+							  size_function, size_function);
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.%s(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.%s(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s",
+							  size_function, size_function);
+		}
+	}
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !show_nested ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "index", "table" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   object_name,
+					   pattern);
+		else
+			/* translator: object_name is "indexes", "tables" */
+			psql_error("Did not find any partitioned %s.\n",
+					  objects_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f38..e3c6dda021 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP[n], \dPi[n], \dPt[n] */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 7c6fa2c590..7137452842 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,7 +168,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -250,6 +250,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7b7a88fda3..928ee9a5cb 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1381,7 +1399,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3503,6 +3521,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 775b127121..2167b14fe5 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4539,3 +4539,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                            List of partitioned tables or indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                      List of partitioned tables or indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPn testpart.*
+                                 List of partitioned tables or indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 1bb2a6e16d..540a416a7b 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1016,3 +1016,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#63Justin Pryzby
pryzby@telsasoft.com
In reply to: Pavel Stehule (#62)
1 attachment(s)
Re: ToDo: show size of partitioned table

On Sat, Feb 16, 2019 at 10:52:35PM +0100, Pavel Stehule wrote:

I like your changes. I merged all - updated patch is attached

I applied and tested your v10 patch.

Find attached some light modifications.

Thanks,
Justin

Attachments:

0001-suggested-fixes-on-top-of-v10-patch.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 9fb632b0bd..4b94c9261a 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1670,17 +1670,18 @@ testdb=&gt;
         partitioned tables are listed; supply a pattern to also include
         partitioned indexes.  If the form <literal>\dP+</literal>
         is used, the sum of sizes of table's partitions (including their
-        indexes) and associated description are also displayed.
+        indexes) is displayed, as is the relation's description.
         </para>
 
         <para>
-         If modifier <literal>n</literal> (which stands for
-         <quote>nested</quote>) is used, then non-root partitioned tables are
-         displayed too.  The displayed size is divided into two columns in
-         this case: one that shows the total size of only the directly
-         attached leaf partitions and another that shows total size of all
-         partitions, also considering other sub-partitioned partitions, for
-         each partitioned tables that's displayed.
+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.
+
+        If <literal>n</literal> is combined with <literal>+</literal>, two
+        sizes are shown: one including the total size of directly-attached
+        leaf partitions, and another showing the total size of all partitions,
+        including indirectly attached sub-partitions.
         </para>
         </listitem>
       </varlistentry>
@@ -1694,11 +1695,11 @@ testdb=&gt;
         class="parameter">pattern</replaceable> is specified, only entries
         whose name matches the pattern are listed. If the form
         <literal>\dPi+</literal> is used, the sum of sizes of index's
-        partitions and associated description are also displayed.
+        partitions is also displayed, along with the associated description.
         </para>
 
         <para>
-         If the modifier <literal>n</literal> is used, non-root partitioned
+         If the <literal>n</literal> modifier is used, non-root partitioned
          indexes are displayed too.
         </para>
         </listitem>
@@ -1713,11 +1714,11 @@ testdb=&gt;
         class="parameter">pattern</replaceable> is specified, only entries
         whose name matches the pattern are listed. If the form
         <literal>\dPt+</literal> is used, the sum of sizes of table's
-        partitions and associated description are also displayed.
+        partitions is also displayed, along with the associated description.
         </para>
 
         <para>
-         If the modifier <literal>n</literal> is used, non-root partitioned
+         If the <literal>n</literal> modifier is used, non-root partitioned
          tables are displayed too.
         </para>
         </listitem>
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2b8628f2ff..6997baedf6 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3768,16 +3768,16 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 		{
 			appendPQExpBuffer(&buf,
 							  ",\n  s.dps as \"%s\"",
-							  gettext_noop("Direct partitions size"));
+							  gettext_noop("Size: leaves"));
 			appendPQExpBuffer(&buf,
 							  ",\n  s.tps as \"%s\"",
-							  gettext_noop("Total partitions size"));
+							  gettext_noop("Size: total"));
 		}
 		else
 			/* Sizes of all partitions are considered in this case. */
 			appendPQExpBuffer(&buf,
 							  ",\n  s.tps as \"%s\"",
-							  gettext_noop("Partitions size"));
+							  gettext_noop("Size: total"));
 
 		appendPQExpBuffer(&buf,
 						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
-- 
2.16.4

#64Pavel Stehule
pavel.stehule@gmail.com
In reply to: Justin Pryzby (#63)
Re: ToDo: show size of partitioned table

čt 21. 2. 2019 v 0:56 odesílatel Justin Pryzby <pryzby@telsasoft.com>
napsal:

On Sat, Feb 16, 2019 at 10:52:35PM +0100, Pavel Stehule wrote:

I like your changes. I merged all - updated patch is attached

I applied and tested your v10 patch.

Find attached some light modifications.

I have not any objection - I cannot to evaluate. It is question for some
native speaker.

I am not sure if we use labels in form "some: detail" somewhere.

Regards

Pavel

Show quoted text

Thanks,
Justin

#65Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#64)
1 attachment(s)
Re: ToDo: show size of partitioned table

On 2019/02/22 1:41, Pavel Stehule wrote:

čt 21. 2. 2019 v 0:56 odesílatel Justin Pryzby <pryzby@telsasoft.com>
napsal:

On Sat, Feb 16, 2019 at 10:52:35PM +0100, Pavel Stehule wrote:

I like your changes. I merged all - updated patch is attached

I applied and tested your v10 patch.

Find attached some light modifications.

I have not any objection - I cannot to evaluate. It is question for some
native speaker.

Not a native speaker either, but I like Justin's changes. Although I
noticed that he missed changing one sentence to look like other similar
sentences.

What Justin did:

-        indexes) and associated description are also displayed.
+        indexes) is displayed, as is the relation's description.
         </para>

What I think he meant to do:

-        indexes) and associated description are also displayed.
+        indexes) is also displayed, along with the associated description.
         </para>

I am not sure if we use labels in form "some: detail" somewhere.

I haven't seen column names like that either. How about:

-                              gettext_noop("Direct partitions size"));
+                              gettext_noop("Leaf partition size"));
-                              gettext_noop("Total partitions size"));
+                              gettext_noop("Total size"));
-                              gettext_noop("Partitions size"));
+                              gettext_noop("Total size"));

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Thanks,
Amit

Attachments:

psql-dP-11.patchtext/plain; charset=UTF-8; name=psql-dP-11.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 1b5d82ed8e..88492ba5d2 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,72 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations.  If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed.  By default, only
+        partitioned tables are listed; supply a pattern to also include
+        partitioned indexes.  If the form <literal>\dP+</literal>
+        is used, the sum of sizes of table's partitions (including their
+        indexes) is also displayed, along with the associated description.
+        </para>
+
+        <para>
+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.
+
+        If <literal>n</literal> is combined with <literal>+</literal>, two
+        sizes are shown: one including the total size of directly-attached
+        leaf partitions, and another showing the total size of all partitions,
+        including indirectly attached sub-partitions.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPi+</literal> is used, the sum of sizes of index's
+        partitions is also displayed, along with the associated description.
+        </para>
+
+        <para>
+         If the <literal>n</literal> modifier is used, non-root partitioned
+         indexes are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPt+</literal> is used, the sum of sizes of table's
+        partitions is also displayed, along with the associated description.
+        </para>
+
+        <para>
+         If the <literal>n</literal> modifier is used, non-root partitioned
+         tables are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ab259c473a..e3d4752857 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,42 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					bool show_nested = strchr(cmd, 'n') ? true : false;
+
+					switch (cmd[2])
+					{
+						case 'i':
+							/* show indexes only */
+							success = listPartitions(pattern, show_verbose,
+													 true, false,
+													 show_nested);
+							break;
+						case 't':
+							/* show tables only */
+							success = listPartitions(pattern, show_verbose,
+													 false, true,
+													 show_nested);
+							break;
+						case '+':
+						case '\0':
+						case 'n':
+							/*
+							 * Show only tables if there is no pattern.  Also
+							 * show indexes if pattern is specified.  Show
+							 * total size if verbose output is specified.
+							 */
+							success = listPartitions(pattern, show_verbose,
+													 false, false,
+													 show_nested);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 779e48437c..4653adf8d5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3673,6 +3673,248 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP[t|i][n]
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes,
+			   bool show_tables, bool show_nested)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *object_name;
+	const char *objects_name;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		psql_error("The server (version %s) does not support declarative table partitioning.\n",
+				   formatPGVersionNumber(pset.sversion, false,
+										 sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		object_name = gettext_noop("index");
+		objects_name = gettext_noop("indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		object_name = gettext_noop("table");
+		objects_name = gettext_noop("tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			object_name = gettext_noop("table");
+			objects_name = gettext_noop("tables");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			object_name = gettext_noop("table or index");
+			objects_name = gettext_noop("tables or indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+		translate_columns[4] = true;
+	}
+
+	if (show_nested)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (show_nested)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Leaf partition size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+		}
+		else
+			/* Sizes of all partitions are considered in this case. */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (show_nested)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.%s(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s",
+							  size_function, size_function);
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.%s(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.%s(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s",
+							  size_function, size_function);
+		}
+	}
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !show_nested ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			/* translator: objects_name is "index", "table" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   object_name,
+					   pattern);
+		else
+			/* translator: object_name is "indexes", "tables" */
+			psql_error("Did not find any partitioned %s.\n",
+					  objects_name);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		/* translator: objects_name is "indexes", "tables" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f38..e3c6dda021 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP[n], \dPi[n], \dPt[n] */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 6fc4ebab1e..49139f856a 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -168,7 +168,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -250,6 +250,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 10ae21cc61..f42a4f4f85 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1381,7 +1399,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3509,6 +3527,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index aa101de906..d4fd761640 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4578,3 +4578,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                            List of partitioned tables or indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                      List of partitioned tables or indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPn testpart.*
+                                 List of partitioned tables or indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index fb7d17fc76..65c30d60e2 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1031,3 +1031,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#66Pavel Stehule
pavel.stehule@gmail.com
In reply to: Amit Langote (#65)
Re: ToDo: show size of partitioned table

st 13. 3. 2019 v 8:02 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

On 2019/02/22 1:41, Pavel Stehule wrote:

čt 21. 2. 2019 v 0:56 odesílatel Justin Pryzby <pryzby@telsasoft.com>
napsal:

On Sat, Feb 16, 2019 at 10:52:35PM +0100, Pavel Stehule wrote:

I like your changes. I merged all - updated patch is attached

I applied and tested your v10 patch.

Find attached some light modifications.

I have not any objection - I cannot to evaluate. It is question for some
native speaker.

Not a native speaker either, but I like Justin's changes. Although I
noticed that he missed changing one sentence to look like other similar
sentences.

What Justin did:

-        indexes) and associated description are also displayed.
+        indexes) is displayed, as is the relation's description.
</para>

What I think he meant to do:

-        indexes) and associated description are also displayed.
+        indexes) is also displayed, along with the associated description.
</para>

I am not sure if we use labels in form "some: detail" somewhere.

I haven't seen column names like that either. How about:

-                              gettext_noop("Direct partitions size"));
+                              gettext_noop("Leaf partition size"));
-                              gettext_noop("Total partitions size"));
+                              gettext_noop("Total size"));
-                              gettext_noop("Partitions size"));
+                              gettext_noop("Total size"));

+1

Pavel

Show quoted text

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Thanks,
Amit

#67Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#66)
Re: ToDo: show size of partitioned table

On 2019/03/14 2:11, Pavel Stehule wrote:

st 13. 3. 2019 v 8:02 odesílatel Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
napsal:

Not a native speaker either, but I like Justin's changes. Although I
noticed that he missed changing one sentence to look like other similar
sentences.

What Justin did:

-        indexes) and associated description are also displayed.
+        indexes) is displayed, as is the relation's description.
</para>

What I think he meant to do:

-        indexes) and associated description are also displayed.
+        indexes) is also displayed, along with the associated description.
</para>

I am not sure if we use labels in form "some: detail" somewhere.

I haven't seen column names like that either. How about:

-                              gettext_noop("Direct partitions size"));
+                              gettext_noop("Leaf partition size"));
-                              gettext_noop("Total partitions size"));
+                              gettext_noop("Total size"));
-                              gettext_noop("Partitions size"));
+                              gettext_noop("Total size"));

+1

Pavel

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

Thanks,
Amit

#68David Steele
david@pgmasters.net
In reply to: Amit Langote (#67)
Re: Re: ToDo: show size of partitioned table

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections. Perhaps you should do that?

Regards,
--
-David
david@pgmasters.net

#69Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: David Steele (#68)
Re: ToDo: show size of partitioned table

On 2019/03/22 2:23, David Steele wrote:

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections.  Perhaps you should do that?

OK, done.

Thanks,
Amit

#70Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Amit Langote (#69)
Re: ToDo: show size of partitioned table

On 2019-03-22 01:21, Amit Langote wrote:

On 2019/03/22 2:23, David Steele wrote:

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections.  Perhaps you should do that?

OK, done.

What is the purpose of this patch (hint: commit message)? The email
subject is "show size of partitioned table", which seems reasonable, but
looking briefly at a patch, it adds new psql commands to display
partitioned tables only. I don't understand the purpose of that.

(Moreover, who is the author of this patch? Is the commit fest entry
accurate?)

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#71Pavel Stehule
pavel.stehule@gmail.com
In reply to: Peter Eisentraut (#70)
Re: ToDo: show size of partitioned table

čt 28. 3. 2019 v 10:12 odesílatel Peter Eisentraut <
peter.eisentraut@2ndquadrant.com> napsal:

On 2019-03-22 01:21, Amit Langote wrote:

On 2019/03/22 2:23, David Steele wrote:

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's

changes and

some of my own on top -- documentation and partition size column

names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections. Perhaps you should do that?

OK, done.

What is the purpose of this patch (hint: commit message)? The email
subject is "show size of partitioned table", which seems reasonable, but
looking briefly at a patch, it adds new psql commands to display
partitioned tables only. I don't understand the purpose of that.

When I started with this patch, main goal was displaying total size of
partitioned tables (sum of size of partitions).

Now, this it can show more - size of indexes, data, - it can aggregate per
partitions levels.

(Moreover, who is the author of this patch? Is the commit fest entry
accurate?)

I am a author, but lot of peoples did modifications and comments.

Regards

Pavel

Show quoted text

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#72Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Peter Eisentraut (#70)
Re: ToDo: show size of partitioned table

Hi,

On 2019/03/28 18:12, Peter Eisentraut wrote:

On 2019-03-22 01:21, Amit Langote wrote:

On 2019/03/22 2:23, David Steele wrote:

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections.  Perhaps you should do that?

OK, done.

What is the purpose of this patch (hint: commit message)? The email
subject is "show size of partitioned table", which seems reasonable, but
looking briefly at a patch, it adds new psql commands to display
partitioned tables only. I don't understand the purpose of that.

(Moreover, who is the author of this patch? Is the commit fest entry
accurate?)

It's mainly Pavel's patch, which I've occasionally posted updated versions
of, when it appeared to me that Pavel might be busy with other stuff.

As mentioned somewhere at the top of this thread, the main reason behind
proposing a new command \dP is that some people didn't much like the idea
that \d+ command itself be modified to gather all partitions and add their
sizes to be shown as the size of the partitioned tables. More features
got added later on to account for partitioned indexes, showing
sub-partitioned tables in addition to root tables that are shown by
default, etc.

Maybe, Pavel can say more...

Thanks,
Amit

#73Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Amit Langote (#72)
Re: ToDo: show size of partitioned table

On 2019/03/28 18:31, Amit Langote wrote:

Hi,

On 2019/03/28 18:12, Peter Eisentraut wrote:

On 2019-03-22 01:21, Amit Langote wrote:

On 2019/03/22 2:23, David Steele wrote:

On 3/14/19 6:19 AM, Amit Langote wrote:

On 2019/03/14 2:11, Pavel Stehule wrote:

I've attached v11 of the patch, which merges most of Justin's changes and
some of my own on top -- documentation and partition size column names.

Maybe, we should set this ready for committer then?

There don't appear to be any objections.  Perhaps you should do that?

OK, done.

What is the purpose of this patch (hint: commit message)? The email
subject is "show size of partitioned table", which seems reasonable, but
looking briefly at a patch, it adds new psql commands to display
partitioned tables only. I don't understand the purpose of that.

(Moreover, who is the author of this patch? Is the commit fest entry
accurate?)

It's mainly Pavel's patch, which I've occasionally posted updated versions
of, when it appeared to me that Pavel might be busy with other stuff.

As mentioned somewhere at the top of this thread, the main reason behind
proposing a new command \dP is that some people didn't much like the idea
that \d+ command itself be modified to gather all partitions and add their
sizes to be shown as the size of the partitioned tables. More features
got added later on to account for partitioned indexes, showing
sub-partitioned tables in addition to root tables that are shown by
default, etc.

Maybe, Pavel can say more...

Ah, his message got in before mine.

Thanks,
Amit

#74Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#65)
Re: ToDo: show size of partitioned table

On 2019-Mar-13, Amit Langote wrote:

+	if (show_indexes)
+	{
+		objects_name = gettext_noop("indexes");

...

+		if (pattern)
+			/* translator: objects_name is "index", "table" */
+			psql_error("Did not find any partitioned %s named \"%s\".\n",
+					   object_name,
+					   pattern);

...

+		/* translator: objects_name is "indexes", "tables" */
+		appendPQExpBuffer(&title, _("List of partitioned %s"), objects_name);
+

This stuff doesn't work from a translation standpoint; sentence building
is not allowed. You have to do stuff like:

if (tables)
{
errmsg = gettext_noop("Could not find any partitioned table");
tabletitle = gettext_noop("List of partitioned tables");
}
...
if (pattern)
/* translator: objects_name is "index", "table" */
psql_error(gettext(errmsg), object_name, pattern);
...
appendPQExpBuffer(&title, gettext(tabletitle));

and so on.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#75Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#74)
Re: ToDo: show size of partitioned table

Something is not right:

alvherre=# \dP t
List of partitioned tables or indexes
Esquema | Nombre | Due�o | Tipo | On table
---------+--------+----------+-------------------+--------------------------------------------------------------
public | t | alvherre | partitioned table | Project-Id-Version: psql (PostgreSQL) 10 +
| | | | Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org +
| | | | PO-Revision-Date: 2017-07-10 12:14-0400 +
| | | | Last-Translator: �lvaro Herrera <alvherre@alvh.no-ip.org> +
| | | | Language-Team: PgSQL Espa�ol <pgsql-es-ayuda@postgresql.org>+
| | | | Language: es +
| | | | MIME-Version: 1.0 +
| | | | Content-Type: text/plain; charset=UTF-8 +
| | | | Content-Transfer-Encoding: 8bit +
| | | | Plural-Forms: nplurals=2; plural=n != 1; +
| | | |
(1 fila)

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#76Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#75)
1 attachment(s)
Re: ToDo: show size of partitioned table

On 2019-Apr-02, Alvaro Herrera wrote:

Something is not right:

Another thing that was not right is that translated_columns was being
marked static, and modified in the function; so if you called \dP twice
where one called for translation and the second not, the second time
we'd also translating that column.

Attached contains fixes for those three issues.

I'm gonna go over the docs now.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

psql-dP-12.patchtext/x-diff; charset=iso-8859-1Download
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b29e7547c6a..a789042ce5f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,72 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations.  If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed.  By default, only
+        partitioned tables are listed; supply a pattern to also include
+        partitioned indexes.  If the form <literal>\dP+</literal>
+        is used, the sum of sizes of table's partitions (including their
+        indexes) is also displayed, along with the associated description.
+        </para>
+
+        <para>
+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.
+
+        If <literal>n</literal> is combined with <literal>+</literal>, two
+        sizes are shown: one including the total size of directly-attached
+        leaf partitions, and another showing the total size of all partitions,
+        including indirectly attached sub-partitions.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPi[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned indexes. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPi+</literal> is used, the sum of sizes of index's
+        partitions is also displayed, along with the associated description.
+        </para>
+
+        <para>
+         If the <literal>n</literal> modifier is used, non-root partitioned
+         indexes are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dPt[n+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned tables. If <replaceable
+        class="parameter">pattern</replaceable> is specified, only entries
+        whose name matches the pattern are listed. If the form
+        <literal>\dPt+</literal> is used, the sum of sizes of table's
+        partitions is also displayed, along with the associated description.
+        </para>
+
+        <para>
+         If the <literal>n</literal> modifier is used, non-root partitioned
+         tables are displayed too.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72188b7f3ef..ce8bee99087 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -782,6 +782,42 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					bool show_nested = strchr(cmd, 'n') ? true : false;
+
+					switch (cmd[2])
+					{
+						case 'i':
+							/* show indexes only */
+							success = listPartitions(pattern, show_verbose,
+													 true, false,
+													 show_nested);
+							break;
+						case 't':
+							/* show tables only */
+							success = listPartitions(pattern, show_verbose,
+													 false, true,
+													 show_nested);
+							break;
+						case '+':
+						case '\0':
+						case 'n':
+							/*
+							 * Show only tables if there is no pattern.  Also
+							 * show indexes if pattern is specified.  Show
+							 * total size if verbose output is specified.
+							 */
+							success = listPartitions(pattern, show_verbose,
+													 false, false,
+													 show_nested);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 036810303a6..736cca33d6b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3774,6 +3774,247 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * listPartitions()
+ *
+ * handler for \dP[t|i][n]
+ */
+bool
+listPartitions(const char *pattern, bool verbose, bool show_indexes,
+			   bool show_tables, bool show_nested)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *errmsg_pattern;
+	const char *errmsg_no_pattern;
+	const char *tabletitle;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support declarative table partitioning.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (show_indexes)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		errmsg_pattern = gettext_noop("Did not find partitioned indexes matching \"%s\".");
+		errmsg_no_pattern = gettext_noop("Did not find partitioned indexes.");
+		tabletitle = gettext_noop("List of partitioned indexes");
+	}
+	else if (show_tables)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		errmsg_pattern = gettext_noop("Did not find partitioned tables matching \"%s\".");
+		errmsg_no_pattern = gettext_noop("Did not find partitioned tables.");
+		tabletitle = gettext_noop("List of partitioned tables");
+	}
+	else
+	{
+		/* \dP without pattern */
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			errmsg_pattern = gettext_noop("Did not find partitioned tables matching \"%s\".");
+			errmsg_no_pattern = gettext_noop("Did not find partitioned tables.");
+			tabletitle = gettext_noop("List of partitioned tables");
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+							", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+			errmsg_pattern = gettext_noop("Did not find partitioned tables or indexes matching \"%s\".");
+			errmsg_no_pattern = gettext_noop("Did not find partitioned tables or indexes.");
+			tabletitle = gettext_noop("List of partitioned tables and indexes");
+			mixed_output = true;
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+					  ",\n  CASE c.relkind"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " END as \"%s\"",
+					  gettext_noop("partitioned table"),
+					  gettext_noop("partitioned index"),
+					  gettext_noop("Type"));
+
+		translate_columns[3] = true;
+	}
+
+	if (show_nested)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (show_indexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (show_nested)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Leaf partition size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+		}
+		else
+			/* Sizes of all partitions are considered in this case. */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (show_indexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (show_nested)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.%s(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s",
+							  size_function, size_function);
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.%s(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.%s(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s",
+							  size_function, size_function);
+		}
+	}
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !show_nested ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	/*
+	 * Most functions in this file are content to print an empty table when
+	 * there are no matching objects.  We intentionally deviate from that
+	 * here, but only in !quiet mode, for historical reasons.
+	 */
+	if (PQntuples(res) == 0 && !pset.quiet)
+	{
+		if (pattern)
+			pg_log_error(errmsg_pattern, pattern);
+		else
+			pg_log_error("%s", errmsg_no_pattern);
+	}
+	else
+	{
+		PQExpBufferData title;
+
+		initPQExpBuffer(&title);
+
+		appendPQExpBuffer(&title, "%s", gettext(tabletitle));
+
+		myopt.nullPrint = NULL;
+		myopt.title = title.data;
+		myopt.translate_header = true;
+		myopt.translate_columns = translate_columns;
+		myopt.n_translate_columns = lengthof(translate_columns);
+
+		printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+		termPQExpBuffer(&title);
+	}
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f381..e3c6dda0213 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP[n], \dPi[n], \dPt[n] */
+extern bool listPartitions(const char *pattern, bool verbose, bool show_indexes, bool show_tables, bool show_nested);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index eb3a0f36d94..32ab0cf525a 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -169,7 +169,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -251,6 +251,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/po/es.po b/src/bin/psql/po/es.po
index 0765be4d6d6..a77c3539dff 100644
--- a/src/bin/psql/po/es.po
+++ b/src/bin/psql/po/es.po
@@ -12,7 +12,7 @@ msgstr ""
 "Project-Id-Version: psql (PostgreSQL) 10\n"
 "Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
 "POT-Creation-Date: 2017-07-05 15:15+0000\n"
-"PO-Revision-Date: 2017-07-10 12:14-0400\n"
+"PO-Revision-Date: 2019-04-02 16:39-0300\n"
 "Last-Translator: Álvaro Herrera <alvherre@alvh.no-ip.org>\n"
 "Language-Team: PgSQL Español <pgsql-es-ayuda@postgresql.org>\n"
 "Language: es\n"
@@ -21,6 +21,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 
+msgid "partitioned table"
+msgstr "table particionada"
+
+msgid "partitioned index"
+msgstr "índice particionado"
+
 #: ../../common/exec.c:127 ../../common/exec.c:241 ../../common/exec.c:284
 #, c-format
 msgid "could not identify current directory: %s"
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index d34bf86fc20..8c46d44a9fa 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1381,7 +1399,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3525,6 +3543,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index aa101de9063..ad6958f9b08 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4578,3 +4578,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                            List of partitioned tables and indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                      List of partitioned tables and indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPn testpart.*
+                                 List of partitioned tables and indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index fb7d17fc76e..65c30d60e24 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1031,3 +1031,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#77Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Alvaro Herrera (#76)
Re: ToDo: show size of partitioned table

Hi Alvaro,

On 2019/04/03 4:42, Alvaro Herrera wrote:

On 2019-Apr-02, Alvaro Herrera wrote:

Something is not right:

Another thing that was not right is that translated_columns was being
marked static, and modified in the function; so if you called \dP twice
where one called for translation and the second not, the second time
we'd also translating that column.

Attached contains fixes for those three issues.

Thanks. I'm getting an error while applying v12:

patching file src/bin/psql/po/es.po
Hunk #1 FAILED at 12.
1 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/po/es.po.rej

Did you accidentally add it to the patch?

Thanks,
Amit

#78Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#77)
Re: ToDo: show size of partitioned table

Hi Amit

On 2019-Apr-03, Amit Langote wrote:

Thanks. I'm getting an error while applying v12:

patching file src/bin/psql/po/es.po
Hunk #1 FAILED at 12.
1 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/po/es.po.rej

Uh, I certainly did not intend to send the es.po part of the patch, I
was just testing that the translation worked as intended. That's what I
get for doing stuff till the last minute ...

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#79Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Alvaro Herrera (#78)
2 attachment(s)
Re: ToDo: show size of partitioned table

At Tue, 2 Apr 2019 22:49:25 -0300, Alvaro Herrera <alvherre@2ndquadrant.com> wrote in <20190403014925.GA9976@alvherre.pgsql>

Hi Amit

On 2019-Apr-03, Amit Langote wrote:

Thanks. I'm getting an error while applying v12:

patching file src/bin/psql/po/es.po
Hunk #1 FAILED at 12.
1 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/po/es.po.rej

Uh, I certainly did not intend to send the es.po part of the patch, I
was just testing that the translation worked as intended. That's what I
get for doing stuff till the last minute ...

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

create table pa (a int, b int) partition by range (a);
create table c1 partition of pa for values from (0) to (10);
create table c2 partition of pa for values from (10) to (20);
create index on pa(a);

postgres=# \dPn *
List of partitioned tables and indexes
Schema | Name | Owner | Type | Parent name | On table
--------+----------+----------+-------------------+-------------+----------
public | pa | horiguti | partitioned table | |
public | pa_a_idx | horiguti | partitioned index | | pa
(2 rows)

With the attached on top of the latest patch, we get the following result.

postgres=# \dPn *
List of partitioned tables and indexes
Schema | Name | Owner | Type | Parent name | On table
--------+----------+----------+-------------------+-------------+----------
public | c1 | horiguti | partition table | pa |
public | c1_a_idx | horiguti | partition index | pa_a_idx | c1
public | c2 | horiguti | partition table | pa |
public | c2_a_idx | horiguti | partition index | pa_a_idx | c2
public | pa | horiguti | partitioned table | |
public | pa_a_idx | horiguti | partitioned index | | pa
(6 rows)

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

Attachments:

psql-dP-12-show-children.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 736cca33d6..5f97bfc4b2 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3954,7 +3954,11 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 		}
 	}
 
-	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	if (show_nested)
+		appendPQExpBuffer(&buf, "\nWHERE (c.relkind IN (%s) OR c3.relkind IN (%s))", relkind_str, relkind_str);
+	else
+		appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+		
 	appendPQExpBufferStr(&buf, !show_nested ? " AND NOT c.relispartition\n" : "\n");
 
 	if (!pattern)
psql-dP-12-show-children-type.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 5f97bfc4b2..41075f9a67 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3864,9 +3864,13 @@ listPartitions(const char *pattern, bool verbose, bool show_indexes,
 					  ",\n  CASE c.relkind"
 					  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
 					  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
+					  " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
 					  " END as \"%s\"",
 					  gettext_noop("partitioned table"),
 					  gettext_noop("partitioned index"),
+					  gettext_noop("partition table"),
+					  gettext_noop("partition index"),
 					  gettext_noop("Type"));
 
 		translate_columns[3] = true;
#80Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Kyotaro HORIGUCHI (#79)
Re: ToDo: show size of partitioned table

Horiguchi-san,

Thanks for taking a look.

On 2019/04/03 12:02, Kyotaro HORIGUCHI wrote:

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

create table pa (a int, b int) partition by range (a);
create table c1 partition of pa for values from (0) to (10);
create table c2 partition of pa for values from (10) to (20);
create index on pa(a);

postgres=# \dPn *
List of partitioned tables and indexes
Schema | Name | Owner | Type | Parent name | On table
--------+----------+----------+-------------------+-------------+----------
public | pa | horiguti | partitioned table | |
public | pa_a_idx | horiguti | partitioned index | | pa
(2 rows)

With the attached on top of the latest patch, we get the following result.

postgres=# \dPn *
List of partitioned tables and indexes
Schema | Name | Owner | Type | Parent name | On table
--------+----------+----------+-------------------+-------------+----------
public | c1 | horiguti | partition table | pa |
public | c1_a_idx | horiguti | partition index | pa_a_idx | c1
public | c2 | horiguti | partition table | pa |
public | c2_a_idx | horiguti | partition index | pa_a_idx | c2
public | pa | horiguti | partitioned table | |
public | pa_a_idx | horiguti | partitioned index | | pa
(6 rows)

I think it's intentional that leaf partitions are not shown, because the
patch is meant to allow showing only the "partitioned" members of
partition trees. Regular \d[t|i][+] shows normal tables ('r', 'i', etc.)
including their size, but it's useless for partitioned tables ('P', 'I',
etc.) as far as showing the size is concerned, so the patch. Even \dPn is
meant to only show partitions that are themselves partitioned; note the
"P" in the command.

Thanks,
Amit

#81Kyotaro HORIGUCHI
horiguchi.kyotaro@lab.ntt.co.jp
In reply to: Amit Langote (#80)
Re: ToDo: show size of partitioned table

Hello.

At Wed, 3 Apr 2019 12:55:06 +0900, Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> wrote in <ee892049-0fe4-afe6-cbbf-31cf44fa8522@lab.ntt.co.jp>

On 2019/04/03 12:02, Kyotaro HORIGUCHI wrote:

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

...

I think it's intentional that leaf partitions are not shown, because the
patch is meant to allow showing only the "partitioned" members of
partition trees. Regular \d[t|i][+] shows normal tables ('r', 'i', etc.)
including their size, but it's useless for partitioned tables ('P', 'I',
etc.) as far as showing the size is concerned, so the patch. Even \dPn is
meant to only show partitions that are themselves partitioned; note the
"P" in the command.

+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.

Ah. I see. "non-root *partitined* tables". I misread the
phrase. Sorry for the noise.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#82Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Kyotaro HORIGUCHI (#81)
Re: ToDo: show size of partitioned table

On 2019-Apr-03, Kyotaro HORIGUCHI wrote:

Hello.

At Wed, 3 Apr 2019 12:55:06 +0900, Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> wrote in <ee892049-0fe4-afe6-cbbf-31cf44fa8522@lab.ntt.co.jp>

On 2019/04/03 12:02, Kyotaro HORIGUCHI wrote:

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

...

I think it's intentional that leaf partitions are not shown, because the
patch is meant to allow showing only the "partitioned" members of
partition trees. Regular \d[t|i][+] shows normal tables ('r', 'i', etc.)
including their size, but it's useless for partitioned tables ('P', 'I',
etc.) as far as showing the size is concerned, so the patch. Even \dPn is
meant to only show partitions that are themselves partitioned; note the
"P" in the command.

+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.

Ah. I see. "non-root *partitined* tables". I misread the
phrase. Sorry for the noise.

Well, is this decision an excellent one, from a UI perspective? I was
surprised by this too and think this should be reconsidered.

I would opt for having \d NOT show partitions at all, myself. When you
have many partitions (and we're now making the system scale into the
thousands for a single partitioned table), they clutter the output
making it unusable. So, rather than think as \dP as "the way to show
the aggregate size of a partitioned table or index", I'd think it as
"the way to show detailed info about a partitioned table or index".
Which includes things like displaying its list of partitions and the
size of each.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#83Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#82)
Re: ToDo: show size of partitioned table

st 3. 4. 2019 v 14:01 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Apr-03, Kyotaro HORIGUCHI wrote:

Hello.

At Wed, 3 Apr 2019 12:55:06 +0900, Amit Langote <

Langote_Amit_f8@lab.ntt.co.jp> wrote in <
ee892049-0fe4-afe6-cbbf-31cf44fa8522@lab.ntt.co.jp>

On 2019/04/03 12:02, Kyotaro HORIGUCHI wrote:

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

...

I think it's intentional that leaf partitions are not shown, because

the

patch is meant to allow showing only the "partitioned" members of
partition trees. Regular \d[t|i][+] shows normal tables ('r', 'i',

etc.)

including their size, but it's useless for partitioned tables ('P',

'I',

etc.) as far as showing the size is concerned, so the patch. Even

\dPn is

meant to only show partitions that are themselves partitioned; note the
"P" in the command.

+ If the modifier <literal>n</literal> (<quote>nested</quote>) is

used,

+ then non-root partitioned tables are included, and a column is

shown

+ displaying the parent of each partitioned relation.

Ah. I see. "non-root *partitined* tables". I misread the
phrase. Sorry for the noise.

I don't need to see root partitioned tables, because this table is empty.

Well, is this decision an excellent one, from a UI perspective? I was
surprised by this too and think this should be reconsidered.

I would opt for having \d NOT show partitions at all, myself. When you
have many partitions (and we're now making the system scale into the
thousands for a single partitioned table), they clutter the output
making it unusable. So, rather than think as \dP as "the way to show
the aggregate size of a partitioned table or index", I'd think it as
"the way to show detailed info about a partitioned table or index".
Which includes things like displaying its list of partitions and the
size of each.

I think so there can be more sensible perspectives how to watch partitioned
data, and how and where you would to see details.

For example - if you remove detail from \d command, then detail should be
displayed in some other command. Now, the detail is in \d and then I need
some aggregation - and it is \dP(*). For me it is not important if is
looking from left to right or from right to left if I see data that I need.
Personally, I have not strong opinion what should be best way - but I am
thinking so some aggregated information over all partitions is necessary.
Currently \d is complex enough, so I designed new command. Any time, this
will be complex task, because we should to display tree structure in a
table.

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#84Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Alvaro Herrera (#82)
Re: ToDo: show size of partitioned table

Hi Alvaro,

On 2019/04/03 21:01, Alvaro Herrera wrote:

On 2019-Apr-03, Kyotaro HORIGUCHI wrote:

Hello.

At Wed, 3 Apr 2019 12:55:06 +0900, Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> wrote in <ee892049-0fe4-afe6-cbbf-31cf44fa8522@lab.ntt.co.jp>

On 2019/04/03 12:02, Kyotaro HORIGUCHI wrote:

\dPn doesn't show children because the are of 'r' relkind. And
\dPn * doesn't show type for children.

...

I think it's intentional that leaf partitions are not shown, because the
patch is meant to allow showing only the "partitioned" members of
partition trees. Regular \d[t|i][+] shows normal tables ('r', 'i', etc.)
including their size, but it's useless for partitioned tables ('P', 'I',
etc.) as far as showing the size is concerned, so the patch. Even \dPn is
meant to only show partitions that are themselves partitioned; note the
"P" in the command.

+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.

Ah. I see. "non-root *partitined* tables". I misread the
phrase. Sorry for the noise.

Well, is this decision an excellent one, from a UI perspective? I was
surprised by this too and think this should be reconsidered.

I would opt for having \d NOT show partitions at all, myself.

Hmm, in the previous long discussions on this topic, people were opposed
[1]: /messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp
main opposition came to the idea that \d+ would perform simple
pg_relation_size for regular tables and something else for partitioned
tables, such as, aggregation query over pg_partition_tree.

When you
have many partitions (and we're now making the system scale into the
thousands for a single partitioned table), they clutter the output
making it unusable. So, rather than think as \dP as "the way to show
the aggregate size of a partitioned table or index", I'd think it as
"the way to show detailed info about a partitioned table or index".
Which includes things like displaying its list of partitions and the
size of each.

That might be a good idea, but maybe there isn't time left to revise the
proposed patch like that for 12?

Thanks,
Amit

[1]: /messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp
/messages/by-id/495cec7e-f8d9-7e13-4807-90dbf4eec4ea@lab.ntt.co.jp

#85Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#84)
Re: ToDo: show size of partitioned table

On 2019-Apr-04, Amit Langote wrote:

When you
have many partitions (and we're now making the system scale into the
thousands for a single partitioned table), they clutter the output
making it unusable. So, rather than think as \dP as "the way to show
the aggregate size of a partitioned table or index", I'd think it as
"the way to show detailed info about a partitioned table or index".
Which includes things like displaying its list of partitions and the
size of each.

That might be a good idea, but maybe there isn't time left to revise the
proposed patch like that for 12?

Well, the problem with designing psql commands incrementallyj is that
people are angry if you try to change what they show in a future
release. We only have one shot to get it right, pretty much.

Looking at the current proposal, I think I like \dPn+ very much -- it
shows the aggregated size of all partitioned tables. But I think \dP
(without the +) is almost completely useless. I'm not really sure about
having to add the "n" flag, but I suspect in practical use it's okay.

Also, I think \dPn+ shows partitioned tables, but \dPtn+ shows exactly
the same, so why do we have the "t" flag at all? We have \dPin+ which
shows aggregate size, but the only way to list both tables and indexes
is to add a pattern. I think this design was confusingly inspired by
the "list" vs. "describe" schizoid dichotomy of \d, without actually
getting any useful functionality in return.

IMO \dP should be "list partitioned relations with their sizes", and
\dPt "list partitioned tables with their sizes", and \dPi "list
partitioned indexes with their sizes". If no pattern is given, list
them all, otherwise only list those that match the pattern.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#86Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#85)
Re: ToDo: show size of partitioned table

On 2019-Apr-05, Alvaro Herrera wrote:

Looking at the current proposal, I think I like \dPn+ very much -- it
shows the aggregated size of all partitioned tables. But I think \dP
(without the +) is almost completely useless. I'm not really sure about
having to add the "n" flag, but I suspect in practical use it's okay.

Also, I think \dPn+ shows partitioned tables, but \dPtn+ shows exactly
the same, so why do we have the "t" flag at all? We have \dPin+ which
shows aggregate size, but the only way to list both tables and indexes
is to add a pattern. I think this design was confusingly inspired by
the "list" vs. "describe" schizoid dichotomy of \d, without actually
getting any useful functionality in return.

IMO \dP should be "list partitioned relations with their sizes", and
\dPt "list partitioned tables with their sizes", and \dPi "list
partitioned indexes with their sizes". If no pattern is given, list
them all, otherwise only list those that match the pattern.

After thinking more about this, I'm having second thoughts about the +
thing. I'm now thinking that requiring the + for computing sizes is
actually a good thing, because if we change it to show all sizes
inconditionally, the command becomes unusable for users with lots of
large partitioned tables. So the submitted patch is okay in that front.

Maybe the only behavior change I'd do to the submitted patch is to have
\dP show both tables and indexes, while \dPt shows only tables and \dPi
shows only indexes. Maybe have \dPti show both tables and indexes? (
identical to \dP) That would be consistent with \d itself. Also,
compare describeFunctions, which allows multiple type specifiers to be
given.

I'm not in love with the way it handles the "n", "t" and "i" specifiers.
I think we should allow them in any order, not just if the "n" is in
cmd[2].

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#87Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#86)
Re: ToDo: show size of partitioned table

so 6. 4. 2019 v 6:36 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Apr-05, Alvaro Herrera wrote:

Looking at the current proposal, I think I like \dPn+ very much -- it
shows the aggregated size of all partitioned tables. But I think \dP
(without the +) is almost completely useless. I'm not really sure about
having to add the "n" flag, but I suspect in practical use it's okay.

Also, I think \dPn+ shows partitioned tables, but \dPtn+ shows exactly
the same, so why do we have the "t" flag at all? We have \dPin+ which
shows aggregate size, but the only way to list both tables and indexes
is to add a pattern. I think this design was confusingly inspired by
the "list" vs. "describe" schizoid dichotomy of \d, without actually
getting any useful functionality in return.

IMO \dP should be "list partitioned relations with their sizes", and
\dPt "list partitioned tables with their sizes", and \dPi "list
partitioned indexes with their sizes". If no pattern is given, list
them all, otherwise only list those that match the pattern.

After thinking more about this, I'm having second thoughts about the +
thing. I'm now thinking that requiring the + for computing sizes is
actually a good thing, because if we change it to show all sizes
inconditionally, the command becomes unusable for users with lots of
large partitioned tables. So the submitted patch is okay in that front.

I remember, there can be another reason - the size can be different on some
platforms, and display size by default can break regress tests.

Maybe the only behavior change I'd do to the submitted patch is to have
\dP show both tables and indexes, while \dPt shows only tables and \dPi
shows only indexes. Maybe have \dPti show both tables and indexes? (
identical to \dP) That would be consistent with \d itself. Also,
compare describeFunctions, which allows multiple type specifiers to be
given.

One my idea (maybe not great) was using total relation size, when user
doesn't specify if would to see tables or indexes. Because we support \dPi
(index size) and \dPt (table size), then I though so for \dP there is a
good possibility to use total relation size.

So now \dP is not same as possible \dPti by design. But I can imagine \dPti
as union of \dPt and \dPi

Currently \dP supports both, without pattern then it show tables with
possible total relation size, with pattern it shows tables and indexes with
related sizes - so behave is very similar to \dPti (but it is driven by
pattern usage).

I'm not in love with the way it handles the "n", "t" and "i" specifiers.
I think we should allow them in any order, not just if the "n" is in
cmd[2].

It is good idea.

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#88Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#87)
1 attachment(s)
Re: ToDo: show size of partitioned table

So how about the attached version?

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

psql-dP-13.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 2bc8bbc2a74..3587abce8b2 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,36 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[itn+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations.  If <replaceable class="parameter">pattern</replaceable>
+        is specified, only entries whose name matches the pattern are listed.
+        The modifiers <literal>t</literal> (tables) and <literal>i</literal>
+        (indexes) can be used, filtering the kind of relations to list.  By
+        default, partitioned tables and indexes are listed.
+        </para>
+        
+        <para>
+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.
+        </para>
+
+        <para>
+        If <literal>+</literal> is appended to the command name, the sum of
+        sizes of table's partitions (including that of their indexes) is also
+        displayed, along with the associated description.
+        If <literal>n</literal> is combined with <literal>+</literal>, two
+        sizes are shown: one including the total size of directly-attached
+        leaf partitions, and another showing the total size of all partitions,
+        including indirectly attached sub-partitions.
+        </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index bc3d10e5158..89f08fc0eda 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -784,6 +784,22 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					switch (cmd[2])
+					{
+						case '\0':
+						case 't':
+						case 'i':
+						case 'n':
+							success = listPartitionedTables(&cmd[2], pattern, show_verbose);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index f7f7285acca..40f753120b6 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3777,6 +3777,235 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * \dP
+ * Takes an optional regexp to select particular relations
+ *
+ * As with \d, you can specify the kinds of relations you want:
+ *
+ * t for tables
+ * i for indexes
+ *
+ * And there's additional flags:
+ *
+ * n to list non-leaf partitioned tables
+ *
+ * and you can mix and match these in any order.
+ */
+bool
+listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
+{
+	bool		showTables = strchr(reltypes, 't') != NULL;
+	bool		showIndexes = strchr(reltypes, 'i') != NULL;
+	bool		showNested = strchr(reltypes, 'n') != NULL;
+	PQExpBufferData buf;
+	PQExpBufferData title;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *size_function;
+	const char *relkind_str;
+	const char *tabletitle;
+	bool		mixed_output = false;
+
+	/* If no relation kind was selected, show them all */
+	if (!showTables && !showIndexes)
+		showTables = showIndexes = true;
+
+	/*
+	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support declarative table partitioning.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	if (showIndexes && !showTables)
+	{
+		/* \dPi */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
+		tabletitle = gettext_noop("List of partitioned indexes");
+	}
+	else if (showTables && !showIndexes)
+	{
+		/* \dPt */
+		size_function = "pg_table_size";
+		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		tabletitle = gettext_noop("List of partitioned tables");
+	}
+	else
+	{
+		/* show all kinds */
+		tabletitle = gettext_noop("List of partitioned tables and indexes");
+		mixed_output = true;
+		if (!pattern)
+		{
+			size_function = "pg_total_relation_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+		}
+		else
+		{
+			size_function = "pg_table_size";
+			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
+				", " CppAsString2(RELKIND_PARTITIONED_INDEX);
+		}
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\n  CASE c.relkind"
+						  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+						  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+						  " END as \"%s\"",
+						  gettext_noop("partitioned table"),
+						  gettext_noop("partitioned index"),
+						  gettext_noop("Type"));
+
+		translate_columns[3] = true;
+	}
+
+	if (showNested)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.relname as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (showIndexes || mixed_output)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.relname as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (showNested)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Leaf partition size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+		}
+		else
+			/* Sizes of all partitions are considered in this case. */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (showIndexes || mixed_output)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (showNested)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.%s("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.%s(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s",
+							  size_function, size_function);
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.%s(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.%s(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s",
+							  size_function, size_function);
+		}
+	}
+
+	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, !showNested ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	initPQExpBuffer(&title);
+	appendPQExpBuffer(&title, "%s", gettext(tabletitle));
+
+	myopt.nullPrint = NULL;
+	myopt.title = title.data;
+	myopt.translate_header = true;
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	termPQExpBuffer(&title);
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f381..17736c37827 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP */
+extern bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 63b115fe018..2d6ef5bd7cb 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -169,7 +169,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -251,6 +251,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7c4e5fbacbe..45e22f276a9 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1382,7 +1400,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3527,6 +3545,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index c8b0ae3ede4..1acbcc22717 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4598,3 +4598,129 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                            List of partitioned tables and indexes
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                      List of partitioned tables and indexes
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+                    List of partitioned tables and indexes
+  Schema  |    Name    |         Owner         |       Type        | On table 
+----------+------------+-----------------------+-------------------+----------
+ testpart | parent_tab | testrole_partitioning | partitioned table | 
+(1 row)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                           List of partitioned tables and indexes
+  Schema  |    Name     |         Owner         |       Type        | Parent name | On table 
+----------+-------------+-----------------------+-------------------+-------------+----------
+ testpart | child_30_40 | testrole_partitioning | partitioned table | parent_tab  | 
+ testpart | parent_tab  | testrole_partitioning | partitioned table |             | 
+(2 rows)
+
+\dPn testpart.*
+                                 List of partitioned tables and indexes
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index a8b2cdc7416..78f4b5d7d50 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1046,3 +1046,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#89Justin Pryzby
pryzby@telsasoft.com
In reply to: Alvaro Herrera (#88)
1 attachment(s)
Re: ToDo: show size of partitioned table

On Sun, Apr 07, 2019 at 08:15:06AM -0400, Alvaro Herrera wrote:

So how about the attached version?

+1

I found a few issues.

\dP+ didn't work. Fix attached.

+static const SchemaQuery Query_for_list_of_partitioned_relations = {                                                                             
+       .catname = "pg_catalog.pg_class c",                                                                                                       
+       .selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),                                                                   

=> Should it be called Query_for_list_of_partitioned_tables ? Or should
c.relkind match indices, too ?

On Sat, Apr 06, 2019 at 01:36:23AM -0300, Alvaro Herrera wrote:

Maybe the only behavior change I'd do to the submitted patch is to have
\dP show both tables and indexes, while \dPt shows only tables and \dPi
shows only indexes. Maybe have \dPti show both tables and indexes? (
identical to \dP) That would be consistent with \d itself.

I think there's an issue with showing indices. You said that \dP should be
same as \dPti, no? Right now, indices are not shown in \dP, unless a pattern
is given. I see you add that behavior in the regression tests; is that really
what's intended ? Also, right now adding a pattern affects how sizes are
computed, I don't see why that's desirable or, if so, how to resolve that
inconsistency, or how to document it.

Justin

Attachments:

psql-dP-13-justin.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 89f08fc..8254d61 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -789,6 +789,7 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 					switch (cmd[2])
 					{
 						case '\0':
+						case '+':
 						case 't':
 						case 'i':
 						case 'n':
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 40f7531..936439e 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3804,7 +3804,6 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 	printQueryOpt myopt = pset.popt;
 	bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
 	const char *size_function;
-	const char *relkind_str;
 	const char *tabletitle;
 	bool		mixed_output = false;
 
@@ -3813,7 +3812,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 		showTables = showIndexes = true;
 
 	/*
-	 * Note: Declarative table partitions are only supported as of Pg 10.0.
+	 * Note: Declarative table partitioning is only supported as of Pg 10.0.
 	 */
 	if (pset.sversion < 100000)
 	{
@@ -3829,14 +3828,12 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 	{
 		/* \dPi */
 		size_function = "pg_table_size";
-		relkind_str = CppAsString2(RELKIND_PARTITIONED_INDEX);
 		tabletitle = gettext_noop("List of partitioned indexes");
 	}
 	else if (showTables && !showIndexes)
 	{
 		/* \dPt */
 		size_function = "pg_table_size";
-		relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
 		tabletitle = gettext_noop("List of partitioned tables");
 	}
 	else
@@ -3844,17 +3841,14 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 		/* show all kinds */
 		tabletitle = gettext_noop("List of partitioned tables and indexes");
 		mixed_output = true;
+		size_function = "pg_table_size";
 		if (!pattern)
 		{
-			size_function = "pg_total_relation_size";
-			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
+			// why ??? size_function = "pg_total_relation_size";
+			// why this too ??? relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE);
 		}
 		else
-		{
 			size_function = "pg_table_size";
-			relkind_str = CppAsString2(RELKIND_PARTITIONED_TABLE)
-				", " CppAsString2(RELKIND_PARTITIONED_INDEX);
-		}
 	}
 
 	initPQExpBuffer(&buf);
@@ -3963,7 +3957,14 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 		}
 	}
 
-	appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (%s)", relkind_str);
+	appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
+	if (showTables)
+		appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
+	if (showIndexes)
+		appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
+	appendPQExpBufferStr(&buf, "''");	/* dummy */
+	appendPQExpBufferStr(&buf, ")\n");
+
 	appendPQExpBufferStr(&buf, !showNested ? " AND NOT c.relispartition\n" : "\n");
 
 	if (!pattern)
#90Pavel Stehule
pavel.stehule@gmail.com
In reply to: Justin Pryzby (#89)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 17:27 odesílatel Justin Pryzby <pryzby@telsasoft.com>
napsal:

On Sun, Apr 07, 2019 at 08:15:06AM -0400, Alvaro Herrera wrote:

So how about the attached version?

+1

I found a few issues.

\dP+ didn't work. Fix attached.

+static const SchemaQuery Query_for_list_of_partitioned_relations = {

+ .catname = "pg_catalog.pg_class c",

+ .selcondition = "c.relkind = "
CppAsString2(RELKIND_PARTITIONED_TABLE),

=> Should it be called Query_for_list_of_partitioned_tables ? Or should
c.relkind match indices, too ?

On Sat, Apr 06, 2019 at 01:36:23AM -0300, Alvaro Herrera wrote:

Maybe the only behavior change I'd do to the submitted patch is to have
\dP show both tables and indexes, while \dPt shows only tables and \dPi
shows only indexes. Maybe have \dPti show both tables and indexes? (
identical to \dP) That would be consistent with \d itself.

I think there's an issue with showing indices. You said that \dP should be
same as \dPti, no? Right now, indices are not shown in \dP, unless a
pattern
is given. I see you add that behavior in the regression tests; is that
really
what's intended ? Also, right now adding a pattern affects how sizes are
computed, I don't see why that's desirable or, if so, how to resolve that
inconsistency, or how to document it.

That depends. If there are not pattern, then \dP show only tables, but with
total relation size (so size of indexes are nested). It is different than
\dPti, but I think so it is useful - when you don't specify object type,
then usually you would to see a tables, but with total size.

I don't see a benefit from \dP == \dPti. When there are a pattern (that can
choose some index, then, indexes are displayed and \dP == \dPti.

I think so Alvaro's version is correct, and I prefer it.

Regards

Pavel

Show quoted text

Justin

#91Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#88)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 14:15 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

So how about the attached version?

+1

Pavel

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#92Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#90)
Re: ToDo: show size of partitioned table

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 17:27 odes�latel Justin Pryzby <pryzby@telsasoft.com>
napsal:

I think there's an issue with showing indices. You said that \dP should be
same as \dPti, no? Right now, indices are not shown in \dP, unless a
pattern is given. I see you add that behavior in the regression
tests; is that really what's intended ? Also, right now adding a
pattern affects how sizes are computed, I don't see why that's
desirable or, if so, how to resolve that inconsistency, or how to
document it.

That depends. If there are not pattern, then \dP show only tables, but with
total relation size (so size of indexes are nested). It is different than
\dPti, but I think so it is useful - when you don't specify object type,
then usually you would to see a tables, but with total size.

I don't see a benefit from \dP == \dPti. When there are a pattern (that can
choose some index, then, indexes are displayed and \dP == \dPti.

Well, I think Justin has it right --- \dP should be just like \df, which
means to list "everything". If you add the "t" or the "i", that means
to list only those kinds of things (just like adding one of a, n, p, t,
w does for \df). You can add both, and then it list both kinds, just
like \dfanptw list the same things that \df does.

That's also what I changed the docs to say, but I failed to update the
code correctly, and didn't verify the expected output closely either.
So I'm due to resubmit this ...

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#93Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#92)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 18:07 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 17:27 odesílatel Justin Pryzby <pryzby@telsasoft.com>
napsal:

I think there's an issue with showing indices. You said that \dP

should be

same as \dPti, no? Right now, indices are not shown in \dP, unless a
pattern is given. I see you add that behavior in the regression
tests; is that really what's intended ? Also, right now adding a
pattern affects how sizes are computed, I don't see why that's
desirable or, if so, how to resolve that inconsistency, or how to
document it.

That depends. If there are not pattern, then \dP show only tables, but

with

total relation size (so size of indexes are nested). It is different than
\dPti, but I think so it is useful - when you don't specify object type,
then usually you would to see a tables, but with total size.

I don't see a benefit from \dP == \dPti. When there are a pattern (that

can

choose some index, then, indexes are displayed and \dP == \dPti.

Well, I think Justin has it right --- \dP should be just like \df, which
means to list "everything". If you add the "t" or the "i", that means
to list only those kinds of things (just like adding one of a, n, p, t,
w does for \df). You can add both, and then it list both kinds, just
like \dfanptw list the same things that \df does.

That's also what I changed the docs to say, but I failed to update the
code correctly, and didn't verify the expected output closely either.
So I'm due to resubmit this ...

ok

Pavel

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#94Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#92)
1 attachment(s)
Re: ToDo: show size of partitioned table

Here.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

psql-dP-14.patchtext/x-diff; charset=utf-8Download
commit 39766894a30d2cbfea7904cb04e9bb0ed398b76c
Author:     Alvaro Herrera <alvherre@alvh.no-ip.org>
AuthorDate: Sun Apr 7 07:59:12 2019 -0400
CommitDate: Sun Apr 7 12:51:13 2019 -0400

    psql \dP: list partitioned tables and indexes
    
    The new command lists partitioned relations (tables and/or indexes),
    possibly with their sizes, possibly including partitioned partitions;
    their parents (if not top-level); if indexes show the tables they belong
    to; and their descriptions.
    
    While there are various possible improvements to this, having it in this
    form is already a great improvement than not having any way to obtain
    this report.
    
    Author: Pavel Stěhule, with help from Mathias Brossard, Amit Langote and
    	Justin Pryzby.
    Reviewed-by: Amit Langote, Mathias Brossard, Melanie Plageman,
    	Michaël Paquier

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 2bc8bbc2a74..3587abce8b2 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1659,6 +1659,36 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
+
+      <varlistentry>
+        <term><literal>\dP[itn+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists partitioned relations.  If <replaceable class="parameter">pattern</replaceable>
+        is specified, only entries whose name matches the pattern are listed.
+        The modifiers <literal>t</literal> (tables) and <literal>i</literal>
+        (indexes) can be used, filtering the kind of relations to list.  By
+        default, partitioned tables and indexes are listed.
+        </para>
+        
+        <para>
+        If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
+        then non-root partitioned tables are included, and a column is shown
+        displaying the parent of each partitioned relation.
+        </para>
+
+        <para>
+        If <literal>+</literal> is appended to the command name, the sum of
+        sizes of table's partitions (including that of their indexes) is also
+        displayed, along with the associated description.
+        If <literal>n</literal> is combined with <literal>+</literal>, two
+        sizes are shown: one including the total size of directly-attached
+        leaf partitions, and another showing the total size of all partitions,
+        including indirectly attached sub-partitions.
+        </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><literal>\drds [ <link linkend="app-psql-patterns"><replaceable class="parameter">role-pattern</replaceable></link> [ <link linkend="app-psql-patterns"><replaceable class="parameter">database-pattern</replaceable></link> ] ]</literal></term>
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index bc3d10e5158..8254d610999 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -784,6 +784,23 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'p':
 				success = permissionsList(pattern);
 				break;
+			case 'P':
+				{
+					switch (cmd[2])
+					{
+						case '\0':
+						case '+':
+						case 't':
+						case 'i':
+						case 'n':
+							success = listPartitionedTables(&cmd[2], pattern, show_verbose);
+							break;
+						default:
+							status = PSQL_CMD_UNKNOWN;
+							break;
+					}
+				}
+				break;
 			case 'T':
 				success = describeTypes(pattern, show_verbose, show_system);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index f7f7285acca..54f5d3fb663 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3777,6 +3777,217 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 	return true;
 }
 
+/*
+ * \dP
+ * Takes an optional regexp to select particular relations
+ *
+ * As with \d, you can specify the kinds of relations you want:
+ *
+ * t for tables
+ * i for indexes
+ *
+ * And there's additional flags:
+ *
+ * n to list non-leaf partitioned tables
+ *
+ * and you can mix and match these in any order.
+ */
+bool
+listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
+{
+	bool		showTables = strchr(reltypes, 't') != NULL;
+	bool		showIndexes = strchr(reltypes, 'i') != NULL;
+	bool		showNested = strchr(reltypes, 'n') != NULL;
+	PQExpBufferData buf;
+	PQExpBufferData title;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
+	const char *tabletitle;
+	bool		mixed_output = false;
+
+	/*
+	 * Note: Declarative table partitioning is only supported as of Pg 10.0.
+	 */
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support declarative table partitioning.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	/* If no relation kind was selected, show them all */
+	if (!showTables && !showIndexes)
+		showTables = showIndexes = true;
+
+	if (showIndexes && !showTables)
+		tabletitle = _("List of partitioned indexes");	/* \dPi */
+	else if (showTables && !showIndexes)
+		tabletitle = _("List of partitioned tables");	/* \dPt */
+	else
+	{
+		/* show all kinds */
+		tabletitle = _("List of partitioned relations");
+		mixed_output = true;
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT n.nspname as \"%s\",\n"
+					  "  c.relname as \"%s\",\n"
+					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Owner"));
+
+	if (mixed_output)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\n  CASE c.relkind"
+						  " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
+						  " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+						  " END as \"%s\"",
+						  gettext_noop("partitioned table"),
+						  gettext_noop("partitioned index"),
+						  gettext_noop("Type"));
+
+		translate_columns[3] = true;
+	}
+
+	if (showNested)
+		appendPQExpBuffer(&buf,
+						  ",\n  c3.oid::regclass as \"%s\"",
+						  gettext_noop("Parent name"));
+
+	if (showIndexes)
+		appendPQExpBuffer(&buf,
+						  ",\n c2.oid::regclass as \"%s\"",
+						  gettext_noop("On table"));
+
+	if (verbose)
+	{
+		if (showNested)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n  s.dps as \"%s\"",
+							  gettext_noop("Leaf partition size"));
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+		}
+		else
+			/* Sizes of all partitions are considered in this case. */
+			appendPQExpBuffer(&buf,
+							  ",\n  s.tps as \"%s\"",
+							  gettext_noop("Total size"));
+
+		appendPQExpBuffer(&buf,
+						  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
+						  gettext_noop("Description"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "\nFROM pg_catalog.pg_class c"
+						 "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
+
+	if (showIndexes)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
+
+	if (showNested)
+		appendPQExpBufferStr(&buf,
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
+							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (WITH RECURSIVE d\n"
+							  "                AS (SELECT inhrelid AS oid, 1 AS level\n"
+							  "                      FROM pg_catalog.pg_inherits\n"
+							  "                     WHERE inhparent = c.oid\n"
+							  "                    UNION ALL\n"
+							  "                    SELECT inhrelid, level + 1\n"
+							  "                      FROM pg_catalog.pg_inherits i\n"
+							  "                           JOIN d ON i.inhparent = d.oid)\n"
+							  "                SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
+							  "d.oid))) AS tps,\n"
+							  "                       pg_catalog.pg_size_pretty(sum("
+							  "\n             CASE WHEN d.level = 1"
+							  " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
+							  "               FROM d) s");
+		}
+		else
+		{
+			/* PostgreSQL 12 has pg_partition_tree function */
+			appendPQExpBuffer(&buf,
+							  ",\n     LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
+							  "\n                 CASE WHEN ppt.isleaf AND ppt.level = 1"
+							  "\n                      THEN pg_catalog.pg_table_size(ppt.relid)"
+							  " ELSE 0 END)) AS dps"
+							  ",\n                     pg_catalog.pg_size_pretty(sum("
+							  "pg_catalog.pg_table_size(ppt.relid))) AS tps"
+							  "\n              FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
+		}
+	}
+
+	appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
+	if (showTables)
+		appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
+	if (showIndexes)
+		appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
+	appendPQExpBufferStr(&buf, "''");	/* dummy */
+	appendPQExpBufferStr(&buf, ")\n");
+
+	appendPQExpBufferStr(&buf, !showNested ? " AND NOT c.relispartition\n" : "\n");
+
+	if (!pattern)
+		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	/*
+	 * TOAST objects are suppressed unconditionally.  Since we don't provide
+	 * any way to select RELKIND_TOASTVALUE above, we would never show toast
+	 * tables in any case; it seems a bit confusing to allow their indexes to
+	 * be shown.  Use plain \d if you really need to look at a TOAST
+	 * table/index.
+	 */
+	appendPQExpBufferStr(&buf, "      AND n.nspname !~ '^pg_toast'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, true, false,
+						  "n.nspname", "c.relname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	initPQExpBuffer(&title);
+	appendPQExpBuffer(&title, "%s", tabletitle);
+
+	myopt.nullPrint = NULL;
+	myopt.title = title.data;
+	myopt.translate_header = true;
+	myopt.translate_columns = translate_columns;
+	myopt.n_translate_columns = lengthof(translate_columns);
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	termPQExpBuffer(&title);
+
+	PQclear(res);
+	return true;
+}
 
 /*
  * \dL
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4ff1f91f381..17736c37827 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -63,6 +63,9 @@ extern bool listAllDbs(const char *pattern, bool verbose);
 /* \dt, \di, \ds, \dS, etc. */
 extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
 
+/* \dP */
+extern bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose);
+
 /* \dD */
 extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 63b115fe018..2d6ef5bd7cb 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -169,7 +169,7 @@ slashUsage(unsigned short int pager)
 	 * Use "psql --help=commands | wc" to count correctly.  It's okay to count
 	 * the USE_READLINE line even in builds without that.
 	 */
-	output = PageOutput(126, pager ? &(pset.popt.topt) : NULL);
+	output = PageOutput(129, pager ? &(pset.popt.topt) : NULL);
 
 	fprintf(output, _("General\n"));
 	fprintf(output, _("  \\copyright             show PostgreSQL usage and distribution terms\n"));
@@ -251,6 +251,9 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\do[S]  [PATTERN]      list operators\n"));
 	fprintf(output, _("  \\dO[S+] [PATTERN]      list collations\n"));
 	fprintf(output, _("  \\dp     [PATTERN]      list table, view, and sequence access privileges\n"));
+	fprintf(output, _("  \\dP[n+]  [PATTERN]     list partitioned relations\n"));
+	fprintf(output, _("  \\dPi[n+] [PATTERN]     list partitioned indexes\n"));
+	fprintf(output, _("  \\dPt[n+] [PATTERN]     list partitioned tables\n"));
 	fprintf(output, _("  \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
 	fprintf(output, _("  \\dRp[+] [PATTERN]      list replication publications\n"));
 	fprintf(output, _("  \\dRs[+] [PATTERN]      list replication subscriptions\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7c4e5fbacbe..45e22f276a9 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -464,6 +464,15 @@ static const SchemaQuery Query_for_list_of_indexes = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+static const SchemaQuery Query_for_list_of_partitioned_indexes = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
+
 /* All relations */
 static const SchemaQuery Query_for_list_of_relations = {
 	.catname = "pg_catalog.pg_class c",
@@ -472,6 +481,15 @@ static const SchemaQuery Query_for_list_of_relations = {
 	.result = "pg_catalog.quote_ident(c.relname)",
 };
 
+/* partitioned relations */
+static const SchemaQuery Query_for_list_of_partitioned_relations = {
+	.catname = "pg_catalog.pg_class c",
+	.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_TABLE),
+	.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+	.namespace = "c.relnamespace",
+	.result = "pg_catalog.quote_ident(c.relname)",
+};
+
 /* Relations supporting INSERT, UPDATE or DELETE */
 static const SchemaQuery Query_for_list_of_updatables = {
 	.catname = "pg_catalog.pg_class c",
@@ -1382,7 +1400,7 @@ psql_completion(const char *text, int start, int end)
 		"\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
 		"\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
-		"\\dm", "\\dn", "\\do", "\\dO", "\\dp",
+		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
 		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
@@ -3527,6 +3545,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
+	else if (TailMatchesCS("\\dPi*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
+	else if (TailMatchesCS("\\dP*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
 	else if (TailMatchesCS("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 	else if (TailMatchesCS("\\dt*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index c8b0ae3ede4..c7392b24985 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4598,3 +4598,132 @@ last error message: division by zero
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 last error code: 22012
 \unset FETCH_COUNT
+create schema testpart;
+create role testrole_partitioning;
+alter schema testpart owner to testrole_partitioning;
+set role to testrole_partitioning;
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+-- only partition related object should be displayed
+\dP test*apple*
+                                List of partitioned relations
+  Schema  |         Name         |         Owner         |       Type        |    On table    
+----------+----------------------+-----------------------+-------------------+----------------
+ testpart | testpart_apple       | testrole_partitioning | partitioned table | 
+ testpart | testpart_apple_index | testrole_partitioning | partitioned index | testpart_apple
+(2 rows)
+
+\dPt test*apple*
+            List of partitioned tables
+  Schema  |      Name      |         Owner         
+----------+----------------+-----------------------
+ testpart | testpart_apple | testrole_partitioning
+(1 row)
+
+\dPi test*apple*
+                       List of partitioned indexes
+  Schema  |         Name         |         Owner         |    On table    
+----------+----------------------+-----------------------+----------------
+ testpart | testpart_apple_index | testrole_partitioning | testpart_apple
+(1 row)
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+\dPt
+          List of partitioned tables
+  Schema  |    Name    |         Owner         
+----------+------------+-----------------------
+ testpart | parent_tab | testrole_partitioning
+(1 row)
+
+\dPi
+                 List of partitioned indexes
+  Schema  |     Name     |         Owner         |  On table  
+----------+--------------+-----------------------+------------
+ testpart | parent_index | testrole_partitioning | parent_tab
+(1 row)
+
+\dP testpart.*
+                          List of partitioned relations
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dP
+                          List of partitioned relations
+  Schema  |     Name     |         Owner         |       Type        |  On table  
+----------+--------------+-----------------------+-------------------+------------
+ testpart | parent_index | testrole_partitioning | partitioned index | parent_tab
+ testpart | parent_tab   | testrole_partitioning | partitioned table | 
+(2 rows)
+
+\dPtn
+                  List of partitioned tables
+  Schema  |    Name     |         Owner         | Parent name 
+----------+-------------+-----------------------+-------------
+ testpart | child_30_40 | testrole_partitioning | parent_tab
+ testpart | parent_tab  | testrole_partitioning | 
+(2 rows)
+
+\dPin
+                            List of partitioned indexes
+  Schema  |        Name        |         Owner         | Parent name  |  On table   
+----------+--------------------+-----------------------+--------------+-------------
+ testpart | child_30_40_id_idx | testrole_partitioning | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning |              | parent_tab
+(2 rows)
+
+\dPn
+                                     List of partitioned relations
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+\dPn testpart.*
+                                     List of partitioned relations
+  Schema  |        Name        |         Owner         |       Type        | Parent name  |  On table   
+----------+--------------------+-----------------------+-------------------+--------------+-------------
+ testpart | child_30_40        | testrole_partitioning | partitioned table | parent_tab   | 
+ testpart | child_30_40_id_idx | testrole_partitioning | partitioned index | parent_index | child_30_40
+ testpart | parent_index       | testrole_partitioning | partitioned index |              | parent_tab
+ testpart | parent_tab         | testrole_partitioning | partitioned table |              | 
+(4 rows)
+
+drop table parent_tab cascade;
+drop schema testpart;
+set search_path to default;
+set role to default;
+drop role testrole_partitioning;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index a8b2cdc7416..78f4b5d7d50 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1046,3 +1046,72 @@ select 1/(15-unique2) from tenk1 order by unique2 limit 19;
 \echo 'last error code:' :LAST_ERROR_SQLSTATE
 
 \unset FETCH_COUNT
+
+create schema testpart;
+create role testrole_partitioning;
+
+alter schema testpart owner to testrole_partitioning;
+
+set role to testrole_partitioning;
+
+-- run test inside own schema and hide other partitions
+set search_path to testpart;
+
+create table testtable_apple(logdate date);
+create table testtable_orange(logdate date);
+create index testtable_apple_index on testtable_apple(logdate);
+create index testtable_orange_index on testtable_orange(logdate);
+
+create table testpart_apple(logdate date) partition by range(logdate);
+create table testpart_orange(logdate date) partition by range(logdate);
+
+create index testpart_apple_index on testpart_apple(logdate);
+create index testpart_orange_index on testpart_orange(logdate);
+
+-- only partition related object should be displayed
+\dP test*apple*
+\dPt test*apple*
+\dPi test*apple*
+
+drop table testtable_apple;
+drop table testtable_orange;
+drop table testpart_apple;
+drop table testpart_orange;
+
+create table parent_tab (id int) partition by range (id);
+create index parent_index on parent_tab (id);
+create table child_0_10 partition of parent_tab
+  for values from (0) to (10);
+create table child_10_20 partition of parent_tab
+  for values from (10) to (20);
+create table child_20_30 partition of parent_tab
+  for values from (20) to (30);
+insert into parent_tab values (generate_series(0,29));
+create table child_30_40 partition of parent_tab
+for values from (30) to (40)
+  partition by range(id);
+create table child_30_35 partition of child_30_40
+  for values from (30) to (35);
+create table child_35_40 partition of child_30_40
+   for values from (35) to (40);
+insert into parent_tab values (generate_series(30,39));
+
+\dPt
+\dPi
+
+\dP testpart.*
+\dP
+
+\dPtn
+\dPin
+\dPn
+\dPn testpart.*
+
+drop table parent_tab cascade;
+
+drop schema testpart;
+
+set search_path to default;
+
+set role to default;
+drop role testrole_partitioning;
#95Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#94)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 19:16 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

Here.

+ 1

Pavel

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#96Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#95)
Re: ToDo: show size of partitioned table

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 19:16 odes�latel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

Here.

+ 1

BTW I'm not sure if you noticed, but I removed the error message "no
partitioned relations found" when the result was empty. This was
mimicking \d behavior (which was justified on historical grounds), and
\drds behavior (which was justified on pattern ordering grounds); but I
see nothing that justifies it for \dP, so let's make it behave like all
the other commands and display an empty table.

And there's an additional change to make. In the regression database,
this returns an empty table:

regression=# \dPi regress_indexing.pk5_pkey
List of partitioned indexes
Esquema | Nombre | Due�o | On table
---------+--------+-------+----------
(0 filas)

but the index does exist, and it is a partitioned one. So it should be
displayed. In fact, if I add the "n" flag, it shows:

regression=# \dPin regress_indexing.pk5_pkey
List of partitioned indexes
Esquema | Nombre | Due�o | Parent name | On table
------------------+----------+----------+--------------------------+----------------------
regress_indexing | pk5_pkey | alvherre | regress_indexing.pk_pkey | regress_indexing.pk5
(1 fila)

I think the fact that \dPi doesn't show it is broken, and this fixes it:

@@ -3946,7 +3946,8 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "''"); /* dummy */
appendPQExpBufferStr(&buf, ")\n");

-	appendPQExpBufferStr(&buf, !showNested ? " AND NOT c.relispartition\n" : "\n");
+	appendPQExpBufferStr(&buf, !showNested && !pattern ?
+						 " AND NOT c.relispartition\n" : "");

if (!pattern)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"

In order for this to display sanely, I added the "Parent name" column if
either the "n" flag is shown or a pattern is given (previously it only
appeared in the former case).

Note that this changes the expected output in the test; now every test
that uses a pattern displays *all* partitioned relations that match the
pattern, not just top-level ones. I'm about +0.2 convinced that this is
desirable, but my first example above tilts the scales to changing it as
described.

I noticed this while testing after messing with the tab completion as
suggested by Justin: we already had Query_for_list_of_partitioned_tables
(which you could have used), but since \dP works for both indexes and
tables, I instead modified your new
Query_for_list_of_partitioned_relations to list both relation kinds.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#97Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#96)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 20:27 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 19:16 odesílatel Alvaro Herrera <

alvherre@2ndquadrant.com>

napsal:

Here.

+ 1

BTW I'm not sure if you noticed, but I removed the error message "no
partitioned relations found" when the result was empty. This was
mimicking \d behavior (which was justified on historical grounds), and
\drds behavior (which was justified on pattern ordering grounds); but I
see nothing that justifies it for \dP, so let's make it behave like all
the other commands and display an empty table.

And there's an additional change to make. In the regression database,
this returns an empty table:

regression=# \dPi regress_indexing.pk5_pkey
List of partitioned indexes
Esquema | Nombre | Dueño | On table
---------+--------+-------+----------
(0 filas)

but the index does exist, and it is a partitioned one. So it should be
displayed. In fact, if I add the "n" flag, it shows:

regression=# \dPin regress_indexing.pk5_pkey
List of partitioned indexes
Esquema | Nombre | Dueño | Parent name |
On table

------------------+----------+----------+--------------------------+----------------------
regress_indexing | pk5_pkey | alvherre | regress_indexing.pk_pkey |
regress_indexing.pk5
(1 fila)

I think the fact that \dPi doesn't show it is broken, and this fixes it:

@@ -3946,7 +3946,8 @@ listPartitionedTables(const char *reltypes, const
char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "''"); /* dummy */
appendPQExpBufferStr(&buf, ")\n");

-       appendPQExpBufferStr(&buf, !showNested ? " AND NOT
c.relispartition\n" : "\n");
+       appendPQExpBufferStr(&buf, !showNested && !pattern ?
+                                                " AND NOT
c.relispartition\n" : "");

if (!pattern)
appendPQExpBufferStr(&buf, " AND n.nspname <>
'pg_catalog'\n"

In order for this to display sanely, I added the "Parent name" column if
either the "n" flag is shown or a pattern is given (previously it only
appeared in the former case).

I am thinking about it and original behave and this new behave should be
expected (and unexpected too). We can go this way - I have not
counter-arguments, but yes, it is more consistent with some other commands,
pattern disables some other constraints.

It should be documented - Using any pattern in this case forces 'n' flag.

Note that this changes the expected output in the test; now every test
that uses a pattern displays *all* partitioned relations that match the
pattern, not just top-level ones. I'm about +0.2 convinced that this is
desirable, but my first example above tilts the scales to changing it as
described.

I noticed this while testing after messing with the tab completion as
suggested by Justin: we already had Query_for_list_of_partitioned_tables
(which you could have used), but since \dP works for both indexes and
tables, I instead modified your new
Query_for_list_of_partitioned_relations to list both relation kinds.

has sense.

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#98Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#97)
Re: ToDo: show size of partitioned table

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 20:27 odes�latel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

In order for this to display sanely, I added the "Parent name" column if
either the "n" flag is shown or a pattern is given (previously it only
appeared in the former case).

I am thinking about it and original behave and this new behave should be
expected (and unexpected too). We can go this way - I have not
counter-arguments, but yes, it is more consistent with some other commands,
pattern disables some other constraints.

It should be documented - Using any pattern in this case forces 'n' flag.

Added to the docs, and pushed.

I couldn't resist tweaking the ORDER BY clause, too. I think listing
all tables first, followed by all indexes, and sorting by parent in each
category, is much easier to read. (Maybe this can use additional
tweaking, but it's a minor thing anyway -- for example putting together
all indexes that correspond to some particular table?)

I noticed that \d never seems to use pg_total_relation_size, so toast
size is never shown. I did likewise here too and used pg_table_size
everywhere. I'm not 100% sure this is the most convenient thing. Maybe
we need yet another column, and/or yet another flag ...?

Also, I think the new \dP should gain a new flag (maybe "l") to make it
list leaf tables/indexes too with their local sizes, and remove those
from the standard \d listing.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#99Justin Pryzby
pryzby@telsasoft.com
In reply to: Alvaro Herrera (#98)
Re: ToDo: show size of partitioned table

On Sun, Apr 07, 2019 at 03:13:36PM -0400, Alvaro Herrera wrote:

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 20:27 odes�latel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

In order for this to display sanely, I added the "Parent name" column if
either the "n" flag is shown or a pattern is given (previously it only
appeared in the former case).

I am thinking about it and original behave and this new behave should be
expected (and unexpected too). We can go this way - I have not
counter-arguments, but yes, it is more consistent with some other commands,
pattern disables some other constraints.

It should be documented - Using any pattern in this case forces 'n' flag.

Added to the docs, and pushed.

Thanks for helping it across the finish line.

I see you changed from relname to oid::regclass, which is good, thanks.

Then, it's unnecessary to join against pg_class again (and I don't know why
it was a left join) ?

Also docs are wrong re: indices.

Justin

#100Justin Pryzby
pryzby@telsasoft.com
In reply to: Justin Pryzby (#99)
1 attachment(s)
Re: ToDo: show size of partitioned table

PFA patch intended to have been previously attached...

Hmm..maybe it should say

| RELATION'S partitions is also displayed, along with the RELATION'S
| description.

Attachments:

fewer-join-and-docs-without-indices.patchtext/x-diff; charset=us-asciiDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 3312570..8d50aef 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1682,8 +1682,8 @@ testdb=&gt;
 
         <para>
         If <literal>+</literal> is appended to the command, the sum of sizes of
-        table's partitions (including that of their indexes) is also displayed,
-        along with the associated description.
+        table's partitions is also displayed, along with the associated
+        description.
         If <literal>n</literal> is combined with <literal>+</literal>, two
         sizes are shown: one including the total size of directly-attached
         leaf partitions, and another showing the total size of all partitions,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 22fb3fb..1ca30fb 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3860,7 +3860,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 
 	if (showNested || pattern)
 		appendPQExpBuffer(&buf,
-						  ",\n  c3.oid::regclass as \"%s\"",
+						  ",\n  inh.inhparent::regclass as \"%s\"",
 						  gettext_noop("Parent name"));
 
 	if (showIndexes)
@@ -3901,8 +3901,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 
 	if (showNested || pattern)
 		appendPQExpBufferStr(&buf,
-							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
-							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
 
 	if (verbose)
 	{
#101Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#98)
Re: ToDo: show size of partitioned table

ne 7. 4. 2019 v 21:13 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

On 2019-Apr-07, Pavel Stehule wrote:

ne 7. 4. 2019 v 20:27 odesílatel Alvaro Herrera <

alvherre@2ndquadrant.com>

napsal:

In order for this to display sanely, I added the "Parent name" column

if

either the "n" flag is shown or a pattern is given (previously it only
appeared in the former case).

I am thinking about it and original behave and this new behave should be
expected (and unexpected too). We can go this way - I have not
counter-arguments, but yes, it is more consistent with some other

commands,

pattern disables some other constraints.

It should be documented - Using any pattern in this case forces 'n' flag.

Added to the docs, and pushed.

Thank you very much

I couldn't resist tweaking the ORDER BY clause, too. I think listing
all tables first, followed by all indexes, and sorting by parent in each
category, is much easier to read. (Maybe this can use additional
tweaking, but it's a minor thing anyway -- for example putting together
all indexes that correspond to some particular table?)

I noticed that \d never seems to use pg_total_relation_size, so toast
size is never shown. I did likewise here too and used pg_table_size
everywhere. I'm not 100% sure this is the most convenient thing. Maybe
we need yet another column, and/or yet another flag ...?

I prefer some flag - both raw size and total size has sense, but another
column will be less readable

Also, I think the new \dP should gain a new flag (maybe "l") to make it
list leaf tables/indexes too with their local sizes, and remove those
from the standard \d listing.

+1

Pavel

Show quoted text

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#102Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Pavel Stehule (#101)
Re: ToDo: show size of partitioned table

Hi,

On 2019/04/08 12:59, Pavel Stehule wrote:

ne 7. 4. 2019 v 21:13 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:

Added to the docs, and pushed.

Thank you very much

Thank you Alvaro for pushing this to completion. Also, thank you Justin
and Pavel for reviewing it till the last minute.

Looks pretty good in my testing so far.

I couldn't resist tweaking the ORDER BY clause, too. I think listing
all tables first, followed by all indexes, and sorting by parent in each
category, is much easier to read. (Maybe this can use additional
tweaking, but it's a minor thing anyway -- for example putting together
all indexes that correspond to some particular table?)

I noticed that \d never seems to use pg_total_relation_size, so toast
size is never shown. I did likewise here too and used pg_table_size
everywhere. I'm not 100% sure this is the most convenient thing. Maybe
we need yet another column, and/or yet another flag ...?

I prefer some flag - both raw size and total size has sense, but another
column will be less readable

I noticed that the size shown in the output of both \dP+ and \dPt+ does
include toast relation sizes of (leaf) partitions, because
pg_table_size(), which is used by all the queries that
listPartitionedTables sends to the server, does:

/*
* heap size, including FSM and VM
*/
for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
forkNum);

/*
* Size of toast relation
*/
if (OidIsValid(rel->rd_rel->reltoastrelid))
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);

Also, I think the new \dP should gain a new flag (maybe "l") to make it
list leaf tables/indexes too with their local sizes, and remove those
from the standard \d listing.

+1

+1 to the idea. Perhaps we'd need to post this in a separate thread to
highlight what may be a substantive change to \d's output?

Thanks,
Amit

#103Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#102)
Re: ToDo: show size of partitioned table

On 2019-Apr-08, Amit Langote wrote:

I noticed that the size shown in the output of both \dP+ and \dPt+ does
include toast relation sizes of (leaf) partitions, because
pg_table_size(), which is used by all the queries that
listPartitionedTables sends to the server, does:

/*
* heap size, including FSM and VM
*/
for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
forkNum);

/*
* Size of toast relation
*/
if (OidIsValid(rel->rd_rel->reltoastrelid))
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);

Sigh. I must have mixed up whether pg_table_size did include toast size
or not. Anyway, I think \dP should report the same thing as \d, which I
think it's doing, so we're good there.

Also, I think the new \dP should gain a new flag (maybe "l") to make it
list leaf tables/indexes too with their local sizes, and remove those
from the standard \d listing.

+1

+1 to the idea. Perhaps we'd need to post this in a separate thread to
highlight what may be a substantive change to \d's output?

Aye aye.

Not in a position to work on that right now; I might get to that in a
few days if nobody beats me to it. (But then, I might not.)

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#104Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Alvaro Herrera (#103)
Re: ToDo: show size of partitioned table

On 2019/04/09 2:37, Alvaro Herrera wrote:

On 2019-Apr-08, Amit Langote wrote:

I noticed that the size shown in the output of both \dP+ and \dPt+ does
include toast relation sizes of (leaf) partitions, because
pg_table_size(), which is used by all the queries that
listPartitionedTables sends to the server, does:

/*
* heap size, including FSM and VM
*/
for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
forkNum);

/*
* Size of toast relation
*/
if (OidIsValid(rel->rd_rel->reltoastrelid))
size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);

Sigh. I must have mixed up whether pg_table_size did include toast size
or not. Anyway, I think \dP should report the same thing as \d, which I
think it's doing, so we're good there.

Yeah.

Also, I think the new \dP should gain a new flag (maybe "l") to make it
list leaf tables/indexes too with their local sizes, and remove those
from the standard \d listing.

+1

+1 to the idea. Perhaps we'd need to post this in a separate thread to
highlight what may be a substantive change to \d's output?

Aye aye.

Not in a position to work on that right now; I might get to that in a
few days if nobody beats me to it. (But then, I might not.)

Sure. I guess I also wanted to hear if you were of thinking it as PG 12
material or PG 13.

Thanks,
Amit

#105Justin Pryzby
pryzby@telsasoft.com
In reply to: Justin Pryzby (#100)
3 attachment(s)
Re: ToDo: show size of partitioned table

A reminder about these.

Also, I suggest renaming "On Table" to "Table", for consistency with \di.

Justin

Attachments:

v2-0001-not-necessary-to-join-pg_class-we-use-oid-regclas.patchtext/x-diff; charset=us-asciiDownload
From e275a0958f0f2cd826e9683fb24b6f757d0fe6c7 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sun, 7 Apr 2019 18:24:22 -0500
Subject: [PATCH v2] not necessary to join pg_class, we use oid::regclass not
 relname

---
 src/bin/psql/describe.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 3a04b06..3940570 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3860,7 +3860,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 
 	if (showNested || pattern)
 		appendPQExpBuffer(&buf,
-						  ",\n  c3.oid::regclass as \"%s\"",
+						  ",\n  inh.inhparent::regclass as \"%s\"",
 						  gettext_noop("Parent name"));
 
 	if (showIndexes)
@@ -3901,8 +3901,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 
 	if (showNested || pattern)
 		appendPQExpBufferStr(&buf,
-							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid"
-							 "\n     LEFT JOIN pg_catalog.pg_class c3 ON c3.oid = inh.inhparent");
+							 "\n     LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
 
 	if (verbose)
 	{
-- 
2.7.4

v2-0001-Refer-to-ptn-relations-not-tables.-Remove-incorre.patchtext/x-diff; charset=us-asciiDownload
From aa499764bd11172785ac9595168e427354f3cd95 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sun, 7 Apr 2019 18:54:04 -0500
Subject: [PATCH v2] Refer to ptn "relations" not tables.  Remove incorrect
 statement "(including that of their indexes)"

---
 doc/src/sgml/ref/psql-ref.sgml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 464c196..08f4bab 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1675,15 +1675,15 @@ testdb=&gt;
 
         <para>
         If the modifier <literal>n</literal> (<quote>nested</quote>) is used,
-        or a pattern is specified, then non-root partitioned tables are
+        or a pattern is specified, then non-root partitioned relations are
         included, and a column is shown displaying the parent of each
         partitioned relation.
         </para>
 
         <para>
-        If <literal>+</literal> is appended to the command, the sum of sizes of
-        table's partitions (including that of their indexes) is also displayed,
-        along with the associated description.
+        If <literal>+</literal> is appended to the command name, the sum of the
+        sizes of each relation's partitions is also displayed, along with the
+        relation's description.
         If <literal>n</literal> is combined with <literal>+</literal>, two
         sizes are shown: one including the total size of directly-attached
         leaf partitions, and another showing the total size of all partitions,
-- 
2.7.4

v1-0001-For-consistency-with-di-rename-column-to-Table.patchtext/x-diff; charset=us-asciiDownload
From eb78f32356c32dbb5b65e69a55177a7a06e863e9 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 17 Apr 2019 08:59:19 -0500
Subject: [PATCH v1] For consistency with \di, rename column to "Table"

---
 src/bin/psql/describe.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 30d1078..0b28d4d 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3885,7 +3885,7 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
 	if (showIndexes)
 		appendPQExpBuffer(&buf,
 						  ",\n c2.oid::regclass as \"%s\"",
-						  gettext_noop("On table"));
+						  gettext_noop("Table"));
 
 	if (verbose)
 	{
-- 
2.7.4

#106Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Justin Pryzby (#105)
Re: ToDo: show size of partitioned table

On 2019-Apr-17, Justin Pryzby wrote:

A reminder about these.

Also, I suggest renaming "On Table" to "Table", for consistency with \di.

Thanks! Pushed all three.

Please feel free to add things like these to wiki.postgresql.org/wiki/Open_Items
to serve as reminders for the project as a whole.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services