list of extended statistics on psql

Started by Tatsuro Yamadaover 5 years ago82 messages
#1Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
1 attachment(s)

Hi!

I created a POC patch that allows showing a list of extended statistics by
"\dz" command on psql. I believe this feature helps DBA and users who
would like to know all extended statistics easily. :-D

I have not a strong opinion to assign "\dz". I prefer "\dx" or "\de*"
than "\dz" but they were already assigned. Therefore I used "\dz"
instead of them.

Please find the attached patch.
Any comments are welcome!

For Example:
=======================
CREATE TABLE t1 (a INT, b INT);
CREATE STATISTICS stts1 (dependencies) ON a, b FROM t1;
CREATE STATISTICS stts2 (dependencies, ndistinct) ON a, b FROM t1;
CREATE STATISTICS stts3 (dependencies, ndistinct, mcv) ON a, b FROM t1;
ANALYZE t1;

CREATE TABLE t2 (a INT, b INT, c INT);
CREATE STATISTICS stts4 ON b, c FROM t2;
ANALYZE t2;

postgres=# \dz
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-------+---------+-----------+--------------+-----
public | t1 | stts1 | a, b | f | t | f
public | t1 | stts2 | a, b | t | t | f
public | t1 | stts3 | a, b | t | t | t
public | t2 | stts4 | b, c | t | t | t
(4 rows)

postgres=# \?
...
\dy [PATTERN] list event triggers
\dz [PATTERN] list extended statistics
\l[+] [PATTERN] list databases
...
=======================

For now, I haven't written a document and regression test for that.
I'll create it later.

Thanks,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_poc1.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_poc1.patchDownload
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a..dc36c98 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -932,6 +932,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
+			case 'z':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			default:
 				status = PSQL_CMD_UNKNOWN;
 		}
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d81f157..8128b1c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4368,6 +4368,67 @@ listEventTriggers(const char *pattern, bool verbose)
 }
 
 /*
+ * \dz
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 10000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT "
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", "
+					  "stxrelid::pg_catalog.regclass AS \"%s\", "
+					  "stxname AS \"%s\", "
+					  "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ') "
+					  "FROM pg_catalog.unnest(stxkeys) s(attnum) "
+					  "JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND "
+					  "a.attnum = s.attnum AND NOT attisdropped)) AS \"%s\", "
+					  "'d' = any(stxkind) AS \"%s\", "
+					  "'f' = any(stxkind) AS \"%s\", "
+					  "'m' = any(stxkind) AS \"%s\"  "
+					  "FROM pg_catalog.pg_statistic_ext stat ",
+					  gettext_noop("Schema"),
+					  gettext_noop("Table"),
+					  gettext_noop("Name"),
+					  gettext_noop("Columns"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
+/*
  * \dC
  *
  * Describes casts.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec9..6c16947 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -105,6 +105,9 @@ extern bool listExtensionContents(const char *pattern);
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
+/* \dz */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dRp */
 bool		listPublications(const char *pattern);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af82928..78b30f2 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -268,6 +268,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
+	fprintf(output, _("  \\dz     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
 	fprintf(output, _("  \\sv[+]  VIEWNAME       show a view's definition\n"));
#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tatsuro Yamada (#1)
Re: list of extended statistics on psql

po 24. 8. 2020 v 5:23 odesílatel Tatsuro Yamada <
tatsuro.yamada.tf@nttcom.co.jp> napsal:

Hi!

I created a POC patch that allows showing a list of extended statistics by
"\dz" command on psql. I believe this feature helps DBA and users who
would like to know all extended statistics easily. :-D

I have not a strong opinion to assign "\dz". I prefer "\dx" or "\de*"
than "\dz" but they were already assigned. Therefore I used "\dz"
instead of them.

Please find the attached patch.
Any comments are welcome!

For Example:
=======================
CREATE TABLE t1 (a INT, b INT);
CREATE STATISTICS stts1 (dependencies) ON a, b FROM t1;
CREATE STATISTICS stts2 (dependencies, ndistinct) ON a, b FROM t1;
CREATE STATISTICS stts3 (dependencies, ndistinct, mcv) ON a, b FROM t1;
ANALYZE t1;

CREATE TABLE t2 (a INT, b INT, c INT);
CREATE STATISTICS stts4 ON b, c FROM t2;
ANALYZE t2;

postgres=# \dz
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-------+---------+-----------+--------------+-----
public | t1 | stts1 | a, b | f | t | f
public | t1 | stts2 | a, b | t | t | f
public | t1 | stts3 | a, b | t | t | t
public | t2 | stts4 | b, c | t | t | t
(4 rows)

postgres=# \?
...
\dy [PATTERN] list event triggers
\dz [PATTERN] list extended statistics
\l[+] [PATTERN] list databases
...
=======================

For now, I haven't written a document and regression test for that.
I'll create it later.

+1 good idea

Pavel

Show quoted text

Thanks,
Tatsuro Yamada

#3Julien Rouhaud
rjuju123@gmail.com
In reply to: Pavel Stehule (#2)
Re: list of extended statistics on psql

On Mon, Aug 24, 2020 at 6:13 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:

po 24. 8. 2020 v 5:23 odesílatel Tatsuro Yamada <tatsuro.yamada.tf@nttcom.co.jp> napsal:

Hi!

I created a POC patch that allows showing a list of extended statistics by
"\dz" command on psql. I believe this feature helps DBA and users who
would like to know all extended statistics easily. :-D

I have not a strong opinion to assign "\dz". I prefer "\dx" or "\de*"
than "\dz" but they were already assigned. Therefore I used "\dz"
instead of them.

Please find the attached patch.
Any comments are welcome!

For Example:
=======================
CREATE TABLE t1 (a INT, b INT);
CREATE STATISTICS stts1 (dependencies) ON a, b FROM t1;
CREATE STATISTICS stts2 (dependencies, ndistinct) ON a, b FROM t1;
CREATE STATISTICS stts3 (dependencies, ndistinct, mcv) ON a, b FROM t1;
ANALYZE t1;

CREATE TABLE t2 (a INT, b INT, c INT);
CREATE STATISTICS stts4 ON b, c FROM t2;
ANALYZE t2;

postgres=# \dz
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-------+---------+-----------+--------------+-----
public | t1 | stts1 | a, b | f | t | f
public | t1 | stts2 | a, b | t | t | f
public | t1 | stts3 | a, b | t | t | t
public | t2 | stts4 | b, c | t | t | t
(4 rows)

postgres=# \?
...
\dy [PATTERN] list event triggers
\dz [PATTERN] list extended statistics
\l[+] [PATTERN] list databases
...
=======================

For now, I haven't written a document and regression test for that.
I'll create it later.

+1 good idea

+1 that's a good idea. Please add it to the next commitfest!

You have a typo:

+    if (pset.sversion < 10000)
+    {
+        char        sverbuf[32];
+
+        pg_log_error("The server (version %s) does not support
extended statistics.",
+                     formatPGVersionNumber(pset.sversion, false,
+                                           sverbuf, sizeof(sverbuf)));
+        return true;
+    }

the version test is missing a 0, the feature looks otherwise ok.

How about using \dX rather than \dz?

#4Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Julien Rouhaud (#3)
1 attachment(s)
Re: list of extended statistics on psql

Hi!

+1 good idea

+1 that's a good idea. Please add it to the next commitfest!

Thanks!

You have a typo:

+    if (pset.sversion < 10000)
+    {
+        char        sverbuf[32];
+
+        pg_log_error("The server (version %s) does not support
extended statistics.",
+                     formatPGVersionNumber(pset.sversion, false,
+                                           sverbuf, sizeof(sverbuf)));
+        return true;
+    }

the version test is missing a 0, the feature looks otherwise ok.

Ouch, I fixed on the attached patch.

The new patch includes:

- Fix the version number check (10000 -> 100000)
- Fix query to get extended stats info for sort order
- Add handling [Pattern] e.g \dz stts*
- Add document and regression test for \dz

How about using \dX rather than \dz?

Thanks for your suggestion!
I'll replace it if I got consensus. :-D

Thanks,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_poc2.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_poc2.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index fc16e6c..d4c9de9 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1909,6 +1909,18 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
+        <term><literal>\dz [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><literal>\e</literal> or <literal>\edit</literal> <literal> <optional> <replaceable class="parameter">filename</replaceable> </optional> <optional> <replaceable class="parameter">line_number</replaceable> </optional> </literal></term>
 
         <listitem>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a..dc36c98 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -932,6 +932,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
+			case 'z':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			default:
 				status = PSQL_CMD_UNKNOWN;
 		}
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d81f157..c9d9e08 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4368,6 +4368,74 @@ listEventTriggers(const char *pattern, bool verbose)
 }
 
 /*
+ * \dz
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "c.relname AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ') \n"
+					  " FROM pg_catalog.unnest(stxkeys) s(attnum) \n"
+					  " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND \n"
+					  " a.attnum = s.attnum AND NOT attisdropped)) AS \"%s\", \n"
+					  "'d' = any(stxkind) AS \"%s\", \n"
+					  "'f' = any(stxkind) AS \"%s\", \n"
+					  "'m' = any(stxkind) AS \"%s\"  \n"
+					  "FROM pg_catalog.pg_statistic_ext \n"
+					  "INNER JOIN pg_catalog.pg_class c \n"
+					  "ON stxrelid = c.oid \n",
+					  gettext_noop("Schema"),
+					  gettext_noop("Table"),
+					  gettext_noop("Name"),
+					  gettext_noop("Columns"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
+/*
  * \dC
  *
  * Describes casts.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec9..6c16947 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -105,6 +105,9 @@ extern bool listExtensionContents(const char *pattern);
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
+/* \dz */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dRp */
 bool		listPublications(const char *pattern);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af82928..78b30f2 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -268,6 +268,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
+	fprintf(output, _("  \\dz     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
 	fprintf(output, _("  \\sv[+]  VIEWNAME       show a view's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 555d464..0c7c780 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5012,3 +5012,41 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+\dz
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+ public | t1    | stts_1    | a, b             | f         | t            | f
+ public | t1    | stts_2    | a, b             | t         | t            | f
+ public | t1    | stts_3    | a, b             | t         | t            | t
+ public | t2    | stts_4    | b, c             | t         | t            | t
+(5 rows)
+
+\dz stts_?
+                    List of extended statistics
+ Schema | Table |  Name  | Columns | Ndistinct | Dependencies | MCV 
+--------+-------+--------+---------+-----------+--------------+-----
+ public | t1    | stts_1 | a, b    | f         | t            | f
+ public | t1    | stts_2 | a, b    | t         | t            | f
+ public | t1    | stts_3 | a, b    | t         | t            | t
+ public | t2    | stts_4 | b, c    | t         | t            | t
+(4 rows)
+
+\dz *hoge
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 5a16080..d8c5688 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1207,3 +1207,20 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+\dz
+\dz stts_?
+\dz *hoge
+drop table t1, t2, hoge;
#5Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#4)
1 attachment(s)
Re: list of extended statistics on psql

Hi Julien and Pavel!

How about using \dX rather than \dz?

Thanks for your suggestion!
I'll replace it if I got consensus. :-D

How about using \dX rather than \dz?

Thanks for your suggestion!
I'll replace it if I got consensus. :-D

I re-read a help message of \d* commands and realized it's better to
use "\dX".
There are already cases where the commands differ due to differences
in case, so I did the same way. Please find attached patch. :-D

For example:
==========
\da[S] [PATTERN] list aggregates
\dA[+] [PATTERN] list access methods
==========

Attached patch uses "\dX" instead of "\dz":
==========
\dx[+] [PATTERN] list extensions
\dX [PATTERN] list extended statistics
==========

Results of regress test of the feature are the following:
==========
-- check printing info about extended statistics
create table t1 (a int, b int);
create statistics stts_1 (dependencies) on a, b from t1;
create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
create table t2 (a int, b int, c int);
create statistics stts_4 on b, c from t2;
create table hoge (col1 int, col2 int, col3 int);
create statistics stts_hoge on col1, col2, col3 from hoge;

\dX
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-----------+------------------+-----------+--------------+-----
public | hoge | stts_hoge | col1, col2, col3 | t | t | t
public | t1 | stts_1 | a, b | f | t | f
public | t1 | stts_2 | a, b | t | t | f
public | t1 | stts_3 | a, b | t | t | t
public | t2 | stts_4 | b, c | t | t | t
(5 rows)

\dX stts_?
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+--------+---------+-----------+--------------+-----
public | t1 | stts_1 | a, b | f | t | f
public | t1 | stts_2 | a, b | t | t | f
public | t1 | stts_3 | a, b | t | t | t
public | t2 | stts_4 | b, c | t | t | t
(4 rows)

\dX *hoge
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-----------+------------------+-----------+--------------+-----
public | hoge | stts_hoge | col1, col2, col3 | t | t | t
(1 row)
==========

Thanks,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_command.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_command.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index fc16e6c..ace8e5f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1893,6 +1893,18 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+	  <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a..dcc9fba 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -929,6 +929,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d81f157..77b3074 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4368,6 +4368,74 @@ listEventTriggers(const char *pattern, bool verbose)
 }
 
 /*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "c.relname AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ') \n"
+					  " FROM pg_catalog.unnest(stxkeys) s(attnum) \n"
+					  " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND \n"
+					  " a.attnum = s.attnum AND NOT attisdropped)) AS \"%s\", \n"
+					  "'d' = any(stxkind) AS \"%s\", \n"
+					  "'f' = any(stxkind) AS \"%s\", \n"
+					  "'m' = any(stxkind) AS \"%s\"  \n"
+					  "FROM pg_catalog.pg_statistic_ext \n"
+					  "INNER JOIN pg_catalog.pg_class c \n"
+					  "ON stxrelid = c.oid \n",
+					  gettext_noop("Schema"),
+					  gettext_noop("Table"),
+					  gettext_noop("Name"),
+					  gettext_noop("Columns"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
+/*
  * \dC
  *
  * Describes casts.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec9..f0b3f91 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af82928..35c39db 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 555d464..caad618 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5012,3 +5012,41 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+\dX
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+ public | t1    | stts_1    | a, b             | f         | t            | f
+ public | t1    | stts_2    | a, b             | t         | t            | f
+ public | t1    | stts_3    | a, b             | t         | t            | t
+ public | t2    | stts_4    | b, c             | t         | t            | t
+(5 rows)
+
+\dX stts_?
+                    List of extended statistics
+ Schema | Table |  Name  | Columns | Ndistinct | Dependencies | MCV 
+--------+-------+--------+---------+-----------+--------------+-----
+ public | t1    | stts_1 | a, b    | f         | t            | f
+ public | t1    | stts_2 | a, b    | t         | t            | f
+ public | t1    | stts_3 | a, b    | t         | t            | t
+ public | t2    | stts_4 | b, c    | t         | t            | t
+(4 rows)
+
+\dX *hoge
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 5a16080..163c1fb 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1207,3 +1207,20 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+\dX
+\dX stts_?
+\dX *hoge
+drop table t1, t2, hoge;
#6Julien Rouhaud
rjuju123@gmail.com
In reply to: Tatsuro Yamada (#5)
Re: list of extended statistics on psql

Hi Yamada-san,

On Thu, Aug 27, 2020 at 03:13:09PM +0900, Tatsuro Yamada wrote:

I re-read a help message of \d* commands and realized it's better to
use "\dX".
There are already cases where the commands differ due to differences
in case, so I did the same way. Please find attached patch. :-D
For example:
==========
\da[S] [PATTERN] list aggregates
\dA[+] [PATTERN] list access methods
==========

Attached patch uses "\dX" instead of "\dz":
==========
\dx[+] [PATTERN] list extensions
\dX [PATTERN] list extended statistics
==========

Thanks for updating the patch! This alias will probably be easier to remember.

Results of regress test of the feature are the following:
==========
-- check printing info about extended statistics
create table t1 (a int, b int);
create statistics stts_1 (dependencies) on a, b from t1;
create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
create table t2 (a int, b int, c int);
create statistics stts_4 on b, c from t2;
create table hoge (col1 int, col2 int, col3 int);
create statistics stts_hoge on col1, col2, col3 from hoge;

\dX
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-----------+------------------+-----------+--------------+-----
public | hoge | stts_hoge | col1, col2, col3 | t | t | t
public | t1 | stts_1 | a, b | f | t | f
public | t1 | stts_2 | a, b | t | t | f
public | t1 | stts_3 | a, b | t | t | t
public | t2 | stts_4 | b, c | t | t | t
(5 rows)

\dX stts_?
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+--------+---------+-----------+--------------+-----
public | t1 | stts_1 | a, b | f | t | f
public | t1 | stts_2 | a, b | t | t | f
public | t1 | stts_3 | a, b | t | t | t
public | t2 | stts_4 | b, c | t | t | t
(4 rows)

\dX *hoge
List of extended statistics
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+-----------+------------------+-----------+--------------+-----
public | hoge | stts_hoge | col1, col2, col3 | t | t | t
(1 row)
==========

Thanks also for the documentation and regression tests. This overall looks
good, I just have a two comments:

- there's a whitespace issue in the documentation part:

add_list_extended_stats_for_psql_by_dX_command.patch:10: tab in indent.
<varlistentry>
warning: 1 line adds whitespace errors.

- You're sorting the output on schema, table, extended statistics and columns
but I think the last one isn't required since extended statistics names are
unique.

#7Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Julien Rouhaud (#6)
1 attachment(s)
Re: list of extended statistics on psql

Hi Julien!

Thanks also for the documentation and regression tests. This overall looks
good, I just have a two comments:

Thank you for reviewing the patch! :-D

- there's a whitespace issue in the documentation part:

add_list_extended_stats_for_psql_by_dX_command.patch:10: tab in indent.
<varlistentry>
warning: 1 line adds whitespace errors.

Oops, I forgot to use "git diff --check". I fixed it.

- You're sorting the output on schema, table, extended statistics and columns
but I think the last one isn't required since extended statistics names are
unique.

You are right.
The sort key "columns" was not necessary so I removed it.

Attached new patch includes the above two fixes:

- Fix whitespace issue in the documentation part
- Remove unnecessary sort key from the query
(ORDER BY 1, 2, 3, 4 -> ORDER BY 1, 2, 3)

Thanks,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_command_r2.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_command_r2.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index fc16e6c..8b0568c 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1893,6 +1893,18 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a..dcc9fba 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -929,6 +929,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d81f157..e43241f 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4368,6 +4368,74 @@ listEventTriggers(const char *pattern, bool verbose)
 }
 
 /*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "c.relname AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ') \n"
+					  " FROM pg_catalog.unnest(stxkeys) s(attnum) \n"
+					  " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND \n"
+					  " a.attnum = s.attnum AND NOT attisdropped)) AS \"%s\", \n"
+					  "'d' = any(stxkind) AS \"%s\", \n"
+					  "'f' = any(stxkind) AS \"%s\", \n"
+					  "'m' = any(stxkind) AS \"%s\"  \n"
+					  "FROM pg_catalog.pg_statistic_ext \n"
+					  "INNER JOIN pg_catalog.pg_class c \n"
+					  "ON stxrelid = c.oid \n",
+					  gettext_noop("Schema"),
+					  gettext_noop("Table"),
+					  gettext_noop("Name"),
+					  gettext_noop("Columns"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
+/*
  * \dC
  *
  * Describes casts.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec9..f0b3f91 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af82928..35c39db 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 555d464..caad618 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5012,3 +5012,41 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+\dX
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+ public | t1    | stts_1    | a, b             | f         | t            | f
+ public | t1    | stts_2    | a, b             | t         | t            | f
+ public | t1    | stts_3    | a, b             | t         | t            | t
+ public | t2    | stts_4    | b, c             | t         | t            | t
+(5 rows)
+
+\dX stts_?
+                    List of extended statistics
+ Schema | Table |  Name  | Columns | Ndistinct | Dependencies | MCV 
+--------+-------+--------+---------+-----------+--------------+-----
+ public | t1    | stts_1 | a, b    | f         | t            | f
+ public | t1    | stts_2 | a, b    | t         | t            | f
+ public | t1    | stts_3 | a, b    | t         | t            | t
+ public | t2    | stts_4 | b, c    | t         | t            | t
+(4 rows)
+
+\dX *hoge
+                          List of extended statistics
+ Schema | Table |   Name    |     Columns      | Ndistinct | Dependencies | MCV 
+--------+-------+-----------+------------------+-----------+--------------+-----
+ public | hoge  | stts_hoge | col1, col2, col3 | t         | t            | t
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 5a16080..163c1fb 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1207,3 +1207,20 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+\dX
+\dX stts_?
+\dX *hoge
+drop table t1, t2, hoge;
#8Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tatsuro Yamada (#7)
Re: list of extended statistics on psql

+1 for the general idea, and +1 for \dX being the syntax to use

IMO the per-type columns should show both the type being enabled as
well as it being built.

(How many more stat types do we expect -- Tomas? I wonder if having one
column per type is going to scale in the long run.)

Also, the stat obj name column should be first, followed by a single
column listing both table and columns that it applies to. Keep in mind
that in the future we might want to add stats that cross multiple tables
-- that's why the CREATE syntax is the way it is. So we should give
room for that in psql's display too.

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

#9Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Alvaro Herrera (#8)
Re: list of extended statistics on psql

Hi Alvaro!

It's been ages since we created a progress reporting feature together. :-D

+1 good idea

+1 that's a good idea. Please add it to the next commitfest!

+1 for the general idea, and +1 for \dX being the syntax to use

Thank you for voting!

IMO the per-type columns should show both the type being enabled as

well as it being built.

Hmm. I'm not sure how to get the status (enabled or disabled) of
extended stats. :(
Could you explain it more?

Also, the stat obj name column should be first, followed by a single
column listing both table and columns that it applies to. Keep in mind
that in the future we might want to add stats that cross multiple tables
-- that's why the CREATE syntax is the way it is. So we should give
room for that in psql's display too.

I understand your suggestions are the following, right?

* The Current column order:
===================
Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+-------+--------+---------+-----------+--------------+-----
public | t1 | stts_1 | a, b | f | t | f
public | t1 | stts_2 | a, b | t | t | f
public | t1 | stts_3 | a, b | t | t | t
public | t2 | stts_4 | b, c | t | t | t
===================

* The suggested column order is like this:
===================
Name | Schema | Table | Columns | Ndistinct | Dependencies | MCV
-----------+--------+-------+------------------+-----------+--------------+-----
stts_1 | public | t1 | a, b | f | t | f
stts_2 | public | t1 | a, b | t | t | f
stts_3 | public | t1 | a, b | t | t | t
stts_4 | public | t2 | b, c | t | t | t
===================

* In the future, Extended stats that cross multiple tables will be
shown maybe... (t1, t2):
===================
Name | Schema | Table | Columns | Ndistinct | Dependencies | MCV
-----------+--------+--------+------------------+-----------+--------------+-----
stts_5 | public | t1, t2 | a, b | f | t | f
===================

If so, I can revise the column order as you suggested easily.
However, I have no idea how to show extended stats that cross
multiple tables and the status now.

I suppose that the current column order is sufficient if there is
no improvement of extended stats on PG14. Do you know any plan to
improve extended stats such as to allow it to cross multiple tables on PG14?

In addition,
Currently, I use this query to get Extended stats info from pg_statistic_ext.

SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
c.relname AS "Table",
stxname AS "Name",
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
FROM pg_catalog.unnest(stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND
a.attnum = s.attnum AND NOT attisdropped)) AS "Columns",
'd' = any(stxkind) AS "Ndistinct",
'f' = any(stxkind) AS "Dependencies",
'm' = any(stxkind) AS "MCV"
FROM pg_catalog.pg_statistic_ext
INNER JOIN pg_catalog.pg_class c
ON stxrelid = c.oid
ORDER BY 1, 2, 3;

Thanks,
Tatsuro Yamada

#10Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tatsuro Yamada (#9)
Re: list of extended statistics on psql

On 2020-Aug-28, Tatsuro Yamada wrote:

IMO the per-type columns should show both the type being enabled as
well as it being built.

Hmm. I'm not sure how to get the status (enabled or disabled) of
extended stats. :(
Could you explain it more?

pg_statistic_ext_data.stxdndistinct is not null if the stats have been
built. (I'm not sure whether there's an easier way to determine this.)

* The suggested column order is like this:
===================
Name | Schema | Table | Columns | Ndistinct | Dependencies | MCV
-----------+--------+-------+------------------+-----------+--------------+-----
stts_1 | public | t1 | a, b | f | t | f
stts_2 | public | t1 | a, b | t | t | f
stts_3 | public | t1 | a, b | t | t | t
stts_4 | public | t2 | b, c | t | t | t
===================

I suggest to do this

Name | Schema | Definition | Ndistinct | Dependencies | MCV
-----------+--------+--------------------------+-----------+--------------+-----
stts_1 | public | (a, b) FROM t1 | f | t | f

I suppose that the current column order is sufficient if there is
no improvement of extended stats on PG14. Do you know any plan to
improve extended stats such as to allow it to cross multiple tables on PG14?

I suggest that changing it in the future is going to be an uphill
battle, so better get it right from the get go, without requiring a
future restructure.

In addition,
Currently, I use this query to get Extended stats info from pg_statistic_ext.

Maybe something like this would do

SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxname AS "Name",
format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
FROM pg_catalog.unnest(stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND
a.attnum = s.attnum AND NOT attisdropped)),
stxrelid::regclass) AS "Definition",
CASE WHEN stxdndistinct IS NOT NULL THEN 'built' WHEN 'd' = any(stxkind) THEN 'enabled, not built' END AS "n-distinct",
CASE WHEN stxddependencies IS NOT NULL THEN 'built' WHEN 'f' = any(stxkind) THEN 'enabled, not built' END AS "functional dependencies",
CASE WHEN stxdmcv IS NOT NULL THEN 'built' WHEN 'm' = any(stxkind) THEN 'enabled, not built' END AS mcv
FROM pg_catalog.pg_statistic_ext es
INNER JOIN pg_catalog.pg_class c
ON stxrelid = c.oid
LEFT JOIN pg_catalog.pg_statistic_ext_data esd ON es.oid = esd.stxoid
ORDER BY 1, 2, 3;

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

#11Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#8)
Re: list of extended statistics on psql

On Thu, Aug 27, 2020 at 07:53:23PM -0400, Alvaro Herrera wrote:

+1 for the general idea, and +1 for \dX being the syntax to use

IMO the per-type columns should show both the type being enabled as
well as it being built.

(How many more stat types do we expect -- Tomas? I wonder if having one
column per type is going to scale in the long run.)

I wouldn't expect a huge number of types. I can imagine maybe twice the
current number of types, but not much more. But I'm not sure the output
is easy to read even now ...

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#12Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#10)
Re: list of extended statistics on psql

On Thu, Aug 27, 2020 at 11:26:17PM -0400, Alvaro Herrera wrote:

On 2020-Aug-28, Tatsuro Yamada wrote:

IMO the per-type columns should show both the type being enabled as
well as it being built.

Hmm. I'm not sure how to get the status (enabled or disabled) of
extended stats. :(
Could you explain it more?

pg_statistic_ext_data.stxdndistinct is not null if the stats have been
built. (I'm not sure whether there's an easier way to determine this.)

It's the only way, I think. Which types were requested is stored in

pg_statistic_ext.stxkind

and what was built is in pg_statistic_ext_data. But if we want the
output to show both what was requested and which types were actually
built, that'll effectively double the number of columns needed :-(

Also, it might be useful to show the size of the statistics built, just
like we show for \d+ etc.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#13Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#12)
Re: list of extended statistics on psql

On 2020-Aug-29, Tomas Vondra wrote:

But if we want the
output to show both what was requested and which types were actually
built, that'll effectively double the number of columns needed :-(

I was thinking it would be one column per type showing either disabled or enabled
or built. But another idea is to show one type per line that's at least
enabled.

Also, it might be useful to show the size of the statistics built, just
like we show for \d+ etc.

\dX+ I suppose?

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

#14Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#13)
Re: list of extended statistics on psql

On Sat, Aug 29, 2020 at 06:43:47PM -0400, Alvaro Herrera wrote:

On 2020-Aug-29, Tomas Vondra wrote:

But if we want the
output to show both what was requested and which types were actually
built, that'll effectively double the number of columns needed :-(

I was thinking it would be one column per type showing either disabled or enabled
or built. But another idea is to show one type per line that's at least
enabled.

Also, it might be useful to show the size of the statistics built, just
like we show for \d+ etc.

\dX+ I suppose?

Right. I've only used \d+ as an example of an existing command showing
sizes of the objects.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#15Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#14)
Re: list of extended statistics on psql

On 2020-Aug-30, Tomas Vondra wrote:

On Sat, Aug 29, 2020 at 06:43:47PM -0400, Alvaro Herrera wrote:

On 2020-Aug-29, Tomas Vondra wrote:

Also, it might be useful to show the size of the statistics built, just
like we show for \d+ etc.

\dX+ I suppose?

Right. I've only used \d+ as an example of an existing command showing
sizes of the objects.

Yeah, I understood it that way too.

How can you measure the size of the stat objects in a query? Are you
thinking in pg_column_size()?

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

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

#16Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#15)
Re: list of extended statistics on psql

On Sun, Aug 30, 2020 at 12:33:29PM -0400, Alvaro Herrera wrote:

On 2020-Aug-30, Tomas Vondra wrote:

On Sat, Aug 29, 2020 at 06:43:47PM -0400, Alvaro Herrera wrote:

On 2020-Aug-29, Tomas Vondra wrote:

Also, it might be useful to show the size of the statistics built, just
like we show for \d+ etc.

\dX+ I suppose?

Right. I've only used \d+ as an example of an existing command showing
sizes of the objects.

Yeah, I understood it that way too.

How can you measure the size of the stat objects in a query? Are you
thinking in pg_column_size()?

Either that or simply length() on the bytea value.

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

I'd use the same approach as \d+, i.e. a separate column with the size.
Maybe that'd mean too many columns, though.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tomas Vondra (#16)
Re: list of extended statistics on psql

Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:

On Sun, Aug 30, 2020 at 12:33:29PM -0400, Alvaro Herrera wrote:

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

Seems a little too cute to me.

I'd use the same approach as \d+, i.e. a separate column with the size.
Maybe that'd mean too many columns, though.

psql already has \d commands with so many columns that you pretty much
have to use \x mode to make them legible; \df+ for instance. I don't
mind if \dX+ is also in that territory. It'd be good though if plain
\dX can fit in a normal terminal window.

regards, tom lane

#18Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Alvaro Herrera (#10)
Re: list of extended statistics on psql

Hi Alvaro,

IMO the per-type columns should show both the type being enabled as
well as it being built.

Hmm. I'm not sure how to get the status (enabled or disabled) of
extended stats. :(
Could you explain it more?

pg_statistic_ext_data.stxdndistinct is not null if the stats have been
built. (I'm not sure whether there's an easier way to determine this.)

Ah.. I see! Thank you.

I suggest to do this

Name | Schema | Definition | Ndistinct | Dependencies | MCV
-----------+--------+--------------------------+-----------+--------------+-----
stts_1 | public | (a, b) FROM t1 | f | t | f

I suppose that the current column order is sufficient if there is
no improvement of extended stats on PG14. Do you know any plan to
improve extended stats such as to allow it to cross multiple tables on PG14?

I suggest that changing it in the future is going to be an uphill
battle, so better get it right from the get go, without requiring a
future restructure.

I understand your suggestions. I'll replace "Columns" and "Table" columns with "Definition" column.

Currently, I use this query to get Extended stats info from pg_statistic_ext.

Maybe something like this would do

SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxname AS "Name",
format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
FROM pg_catalog.unnest(stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND
a.attnum = s.attnum AND NOT attisdropped)),
stxrelid::regclass) AS "Definition",
CASE WHEN stxdndistinct IS NOT NULL THEN 'built' WHEN 'd' = any(stxkind) THEN 'enabled, not built' END AS "n-distinct",
CASE WHEN stxddependencies IS NOT NULL THEN 'built' WHEN 'f' = any(stxkind) THEN 'enabled, not built' END AS "functional dependencies",
CASE WHEN stxdmcv IS NOT NULL THEN 'built' WHEN 'm' = any(stxkind) THEN 'enabled, not built' END AS mcv
FROM pg_catalog.pg_statistic_ext es
INNER JOIN pg_catalog.pg_class c
ON stxrelid = c.oid
LEFT JOIN pg_catalog.pg_statistic_ext_data esd ON es.oid = esd.stxoid
ORDER BY 1, 2, 3;

Great! It helped me a lot to understand your suggestions correctly. Thanks. :-D
I got the below results by your query.

========
create table t1 (a int, b int);
create statistics stts_1 (dependencies) on a, b from t1;
create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
create table t2 (a int, b int, c int);
create statistics stts_4 on b, c from t2;
create table hoge (col1 int, col2 int, col3 int);
create statistics stts_hoge on col1, col2, col3 from hoge;

insert into t1 select i,i from generate_series(1,100) i;
analyze t1;

Your query gave this result:

Schema | Name | Definition | n-distinct | functional dependencies | mcv
--------+-----------+----------------------------+--------------------+-------------------------+--------------------
public | stts_1 | a, b FROM t1 | | built |
public | stts_2 | a, b FROM t1 | built | built |
public | stts_3 | a, b FROM t1 | built | built | built
public | stts_4 | b, c FROM t2 | enabled, not built | enabled, not built | enabled, not built
public | stts_hoge | col1, col2, col3 FROM hoge | enabled, not built | enabled, not built | enabled, not built
(5 rows)
========

I guess "enabled, not built" is a little redundant. The status would better to
have three patterns: "built", "not built" or nothing (NULL) like these:

- "built": extended stats is defined and built (collected by analyze cmd)
- "not built": extended stats is defined but have not built yet
- nothing (NULL): extended stats is not defined

What do you think about it?

I will send a new patch including :

- Replace "Columns" and "Table" column with "Definition"
- Show the status (built/not built/null) of extended stats by using
pg_statistic_ext_data

Thanks,
Tatsuro Yamada

#19Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tom Lane (#17)
1 attachment(s)
Re: list of extended statistics on psql

On 2020/08/31 1:59, Tom Lane wrote:

Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:

On Sun, Aug 30, 2020 at 12:33:29PM -0400, Alvaro Herrera wrote:

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

Seems a little too cute to me.

I'd use the same approach as \d+, i.e. a separate column with the size.
Maybe that'd mean too many columns, though.

psql already has \d commands with so many columns that you pretty much
have to use \x mode to make them legible; \df+ for instance. I don't
mind if \dX+ is also in that territory. It'd be good though if plain
\dX can fit in a normal terminal window.

Hmm. How about these instead of "built: %d bytes"?
I added three columns (N_size, D_size, M_size) to show size. See below:

===================
postgres=# \dX
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv
--------+-----------+----------------------------+------------+--------------+-----------
public | stts_1 | a, b FROM t1 | | built |
public | stts_2 | a, b FROM t1 | built | built |
public | stts_3 | a, b FROM t1 | built | built | built
public | stts_4 | b, c FROM t2 | not built | not built | not built
public | stts_hoge | col1, col2, col3 FROM hoge | not built | not built | not built
(5 rows)

postgres=# \dX+
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size
--------+-----------+----------------------------+------------+--------------+-----------+--------+--------+--------
public | stts_1 | a, b FROM t1 | | built | | | 40 |
public | stts_2 | a, b FROM t1 | built | built | | 13 | 40 |
public | stts_3 | a, b FROM t1 | built | built | built | 13 | 40 | 6126
public | stts_4 | b, c FROM t2 | not built | not built | not built | | |
public | stts_hoge | col1, col2, col3 FROM hoge | not built | not built | not built | | |
===================

I used this query to get results of "\dX+".
===================
SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxname AS "Name",
format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
FROM pg_catalog.unnest(stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a
ON (stxrelid = a.attrelid
AND a.attnum = s.attnum
AND NOT attisdropped)),
stxrelid::regclass) AS "Definition",
CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built'
WHEN 'd' = any(stxkind) THEN 'not built'
END AS "N_distinct",
CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built'
WHEN 'f' = any(stxkind) THEN 'not built'
END AS "Dependencies",
CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built'
WHEN 'm' = any(stxkind) THEN 'not built'
END AS "Mcv",
pg_catalog.length(stxdndistinct) AS "N_size",
pg_catalog.length(stxddependencies) AS "D_size",
pg_catalog.length(stxdmcv) AS "M_size"
FROM pg_catalog.pg_statistic_ext es
INNER JOIN pg_catalog.pg_class c
ON stxrelid = c.oid
LEFT JOIN pg_catalog.pg_statistic_ext_data esd
ON es.oid = esd.stxoid
ORDER BY 1, 2;
===================

Attached patch includes:

- Replace "Columns" and "Table" column with "Definition"
- Show the status (built/not built/null) of extended stats by
using pg_statistic_ext_data
- Add "\dX+" command to show size of extended stats

Please find the attached file! :-D

Thanks,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r3.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r3.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index fc16e6c..5664c22 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1893,6 +1893,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9902a4a..077a585 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -929,6 +929,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d81f157..d99e387 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4368,6 +4368,98 @@ listEventTriggers(const char *pattern, bool verbose)
 }
 
 /*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ') \n"
+					  "   FROM pg_catalog.unnest(stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT attisdropped)), \n"
+					  "  stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(stxkind) THEN 'not built' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(stxkind) THEN 'not built' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(stxkind) THEN 'not built' \n"
+					  "END AS \"%s\" \n",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\n pg_catalog.length(stxdndistinct) AS \"%s\", \n"
+						  "pg_catalog.length(stxddependencies) AS \"%s\", \n"
+						  "pg_catalog.length(stxdmcv) AS \"%s\" \n",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+
+	}
+
+	appendPQExpBufferStr(&buf,
+						 "FROM pg_catalog.pg_statistic_ext es \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON stxrelid = c.oid \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
+/*
  * \dC
  *
  * Describes casts.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec9..89b13c3 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af82928..ea249bf 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 555d464..76facc4 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5012,3 +5012,71 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                               List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |    Mcv    
+--------+-----------+----------------------------+------------+--------------+-----------
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | not built  | not built    | not built
+ public | stts_hoge | col1, col2, col3 FROM hoge | not built  | not built    | not built
+(5 rows)
+
+\dX stts_?
+                      List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |    Mcv    
+--------+--------+--------------+------------+--------------+-----------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | not built  | not built    | not built
+(4 rows)
+
+\dX *hoge
+                               List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |    Mcv    
+--------+-----------+----------------------------+------------+--------------+-----------
+ public | stts_hoge | col1, col2, col3 FROM hoge | not built  | not built    | not built
+(1 row)
+
+\dX+
+                                            List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |    Mcv    | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+-----------+--------+--------+--------
+ public | stts_1    | a, b FROM t1               |            | built        |           |        |     40 |       
+ public | stts_2    | a, b FROM t1               | built      | built        |           |     13 |     40 |       
+ public | stts_3    | a, b FROM t1               | built      | built        | built     |     13 |     40 |   6126
+ public | stts_4    | b, c FROM t2               | not built  | not built    | not built |        |        |       
+ public | stts_hoge | col1, col2, col3 FROM hoge | not built  | not built    | not built |        |        |       
+(5 rows)
+
+\dX+ stts_?
+                                    List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |    Mcv    | N_size | D_size | M_size 
+--------+--------+--------------+------------+--------------+-----------+--------+--------+--------
+ public | stts_1 | a, b FROM t1 |            | built        |           |        |     40 |       
+ public | stts_2 | a, b FROM t1 | built      | built        |           |     13 |     40 |       
+ public | stts_3 | a, b FROM t1 | built      | built        | built     |     13 |     40 |   6126
+ public | stts_4 | b, c FROM t2 | not built  | not built    | not built |        |        |       
+(4 rows)
+
+\dX+ *hoge
+                                            List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |    Mcv    | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+-----------+--------+--------+--------
+ public | stts_hoge | col1, col2, col3 FROM hoge | not built  | not built    | not built |        |        |       
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 5a16080..82f49a1 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1207,3 +1207,26 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
#20Justin Pryzby
pryzby@telsasoft.com
In reply to: Alvaro Herrera (#8)
Re: list of extended statistics on psql

On Thu, Aug 27, 2020 at 07:53:23PM -0400, Alvaro Herrera wrote:

+1 for the general idea, and +1 for \dX being the syntax to use

IMO the per-type columns should show both the type being enabled as
well as it being built.

(How many more stat types do we expect -- Tomas? I wonder if having one
column per type is going to scale in the long run.)

Also, the stat obj name column should be first, followed by a single
column listing both table and columns that it applies to. Keep in mind
that in the future we might want to add stats that cross multiple tables
-- that's why the CREATE syntax is the way it is. So we should give
room for that in psql's display too.

There's also a plan for CREATE STATISTICS to support expresion statistics, with
the statistics functionality of an expression index, but without the cost of
index-update on UPDATE/DELETE. That's Tomas' patch here:
https://commitfest.postgresql.org/29/2421/

I think that would compute ndistinct and MCV, same as indexes, but not
dependencies. To me, I think it's better if there's a single column showing
the "kinds" of statistics to be generated (stxkind), rather than a column for
each.

I'm not sure why the length of the stats lists cast as text is useful to show?
We don't have a slash-dee command to show the number of MCV or histogram in
traditional, 1-D stats in pg_statistic, right ? I think anybody wanting that
would learn to SELECT FROM pg_statistic*. Also, the length of the text output
isn't very meaningful ? If this is json, maybe you'd do something like this:
|SELECT a.stxdndistinct , COUNT(b) FROM pg_statistic_ext_data a , json_each(stxdndistinct::Json) AS b GROUP BY 1

I guess stxdmcv isn't json, but it seems especially meaningless to show
length() of its ::text, since we don't even "deserialize" the object to begin
with.

BTW, I've just started a new thread about displaying in psql \d the stats
target of target extended stats.

--
Justin

#21Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#16)
Re: list of extended statistics on psql

On 2020-Aug-30, Tomas Vondra wrote:

On Sun, Aug 30, 2020 at 12:33:29PM -0400, Alvaro Herrera wrote:

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

I'd use the same approach as \d+, i.e. a separate column with the size.
Maybe that'd mean too many columns, though.

Are you thinking in one size for all stats, or a combined size? If the
former, then yes it'd be too many columns.

I'm trying to figure out what can the user *do* with that data. Can
they make the sample size smaller/bigger if the stats data is too large?
Can they do that for each individual stats type? If so, it'd make sense
to list each type's size separately.

If we do put each type in its own row -- at least "logical" row, say
string_agg(unnest(array_of_types), '\n') -- then we can put the size of each type
in a separate column with string_agg(unnest(array_of_sizes), '\n')

statname | definition | type | size
----------+-----------------+--------------------------+-----------
someobj | (a, b) FROM tab | n-distinct: built | 2000 bytes
| func-dependencies: built | 4000 bytes
another | (a, c) FROM tab | n-distint: enabled | <null>

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

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#21)
Re: list of extended statistics on psql

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

If we do put each type in its own row -- at least "logical" row, say
string_agg(unnest(array_of_types), '\n') -- then we can put the size of each type
in a separate column with string_agg(unnest(array_of_sizes), '\n')

statname | definition | type | size
----------+-----------------+--------------------------+-----------
someobj | (a, b) FROM tab | n-distinct: built | 2000 bytes
| func-dependencies: built | 4000 bytes
another | (a, c) FROM tab | n-distint: enabled | <null>

I guess I'm wondering why the size is of such interest that we
need it at all here.

regards, tom lane

#23Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Tom Lane (#22)
Re: list of extended statistics on psql

On Mon, Aug 31, 2020 at 10:58:11AM -0400, Tom Lane wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

If we do put each type in its own row -- at least "logical" row, say
string_agg(unnest(array_of_types), '\n') -- then we can put the size of each type
in a separate column with string_agg(unnest(array_of_sizes), '\n')

statname | definition | type | size
----------+-----------------+--------------------------+-----------
someobj | (a, b) FROM tab | n-distinct: built | 2000 bytes
| func-dependencies: built | 4000 bytes
another | (a, c) FROM tab | n-distint: enabled | <null>

I guess I'm wondering why the size is of such interest that we
need it at all here.

I agree it may not be important enough. I did use it during development
etc. but maybe it's not something we need to include in this list (even
if it's just in the \dX+ variant).

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#24Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#21)
Re: list of extended statistics on psql

On Mon, Aug 31, 2020 at 10:28:38AM -0400, Alvaro Herrera wrote:

On 2020-Aug-30, Tomas Vondra wrote:

On Sun, Aug 30, 2020 at 12:33:29PM -0400, Alvaro Herrera wrote:

I wonder how to report that. Knowing that psql \-commands are not meant
for anything other than human consumption, maybe we can use a format()
string that says "built: %d bytes" when \dX+ is used (for each stat type),
and just "built" when \dX is used. What do people think about this?

I'd use the same approach as \d+, i.e. a separate column with the size.
Maybe that'd mean too many columns, though.

Are you thinking in one size for all stats, or a combined size? If the
former, then yes it'd be too many columns.

I wonder if trying to list info about all stats from the statistics
object in a single line is necessary. Maybe we should split the info
into one line per statistics, so for example

CREATE STATISTICS s (mcv, ndistinct, dependencies) ON ...

would result in three lines in the \dX output. The statistics name would
identify which lines belong together, but other than that the pieces are
mostly independent.

This would make it somewhat future-proof in case we add more statistics
types, because the number of columns would not increase. OTOH maybe it's
pointless and/or against the purpose of listing statistics objects.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#25Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#24)
Re: list of extended statistics on psql

On 2020-Aug-31, Tomas Vondra wrote:

I wonder if trying to list info about all stats from the statistics
object in a single line is necessary. Maybe we should split the info
into one line per statistics, so for example

CREATE STATISTICS s (mcv, ndistinct, dependencies) ON ...

would result in three lines in the \dX output. The statistics name would
identify which lines belong together, but other than that the pieces are
mostly independent.

Yeah, that's what I'm suggesting. I don't think we need to repeat the
name/definition for each line though.

It might be useful to know how does pspg show a single entry that's
split in three lines, though.

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

#26Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Alvaro Herrera (#25)
Re: list of extended statistics on psql

On Mon, Aug 31, 2020 at 12:18:09PM -0400, Alvaro Herrera wrote:

On 2020-Aug-31, Tomas Vondra wrote:

I wonder if trying to list info about all stats from the statistics
object in a single line is necessary. Maybe we should split the info
into one line per statistics, so for example

CREATE STATISTICS s (mcv, ndistinct, dependencies) ON ...

would result in three lines in the \dX output. The statistics name would
identify which lines belong together, but other than that the pieces are
mostly independent.

Yeah, that's what I'm suggesting. I don't think we need to repeat the
name/definition for each line though.

It might be useful to know how does pspg show a single entry that's
split in three lines, though.

Ah, I didn't realize you're proposing that - I assumed it's broken
simply to make it readable, or something like that. I think the lines
are mostly independent, so I'd suggest to include the name of the object
on each line. The question is whether this independence will remain true
in the future - for example histograms would be built only on data not
represented by the MCV list, so there's a close dependency there.

Not sure about pspg, and I'm not sure it matters too much.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#27Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tomas Vondra (#26)
Re: list of extended statistics on psql

po 31. 8. 2020 v 18:32 odesílatel Tomas Vondra <tomas.vondra@2ndquadrant.com>
napsal:

On Mon, Aug 31, 2020 at 12:18:09PM -0400, Alvaro Herrera wrote:

On 2020-Aug-31, Tomas Vondra wrote:

I wonder if trying to list info about all stats from the statistics
object in a single line is necessary. Maybe we should split the info
into one line per statistics, so for example

CREATE STATISTICS s (mcv, ndistinct, dependencies) ON ...

would result in three lines in the \dX output. The statistics name would
identify which lines belong together, but other than that the pieces are
mostly independent.

Yeah, that's what I'm suggesting. I don't think we need to repeat the
name/definition for each line though.

It might be useful to know how does pspg show a single entry that's
split in three lines, though.

Ah, I didn't realize you're proposing that - I assumed it's broken
simply to make it readable, or something like that. I think the lines
are mostly independent, so I'd suggest to include the name of the object
on each line. The question is whether this independence will remain true
in the future - for example histograms would be built only on data not
represented by the MCV list, so there's a close dependency there.

Not sure about pspg, and I'm not sure it matters too much.

pspg almost ignores multiline rows - the horizontal cursor is one row every
time. There is only one use case where pspg detects multiline rows - sorts,
and pspg ensures correct content for multiline rows displayed in different
(than input) order.

Regards

Pavel

Show quoted text

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#28Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Pavel Stehule (#27)
Re: list of extended statistics on psql

Hi,

I wonder if trying to list info about all stats from the statistics
object in a single line is necessary. Maybe we should split the info
into one line per statistics, so for example

     CREATE STATISTICS s (mcv, ndistinct, dependencies) ON ...

would result in three lines in the \dX output. The statistics name would
identify which lines belong together, but other than that the pieces are
mostly independent.

Yeah, that's what I'm suggesting.  I don't think we need to repeat the
name/definition for each line though.

It might be useful to know how does pspg show a single entry that's
split in three lines, though.

Ah, I didn't realize you're proposing that - I assumed it's broken
simply to make it readable, or something like that. I think the lines
are mostly independent, so I'd suggest to include the name of the object
on each line. The question is whether this independence will remain true
in the future - for example histograms would be built only on data not
represented by the MCV list, so there's a close dependency there.

Not sure about pspg, and I'm not sure it matters too much.

pspg almost ignores multiline rows - the horizontal cursor is one row every time. There is only one use case where pspg detects multiline rows - sorts, and pspg ensures correct content for multiline rows displayed in different (than input) order.

I try to summarize the discussion so far.
Is my understanding right? Could you revise it if it has something wrong?

* Summary

1. "\dX[+]" doesn't display the Size of extended stats since the size is
useful only for the development process of the stats.

2. each row should have stats name, definition, type, and status.
For example:

statname | definition | type |
----------+------------------+---------------------------+
someobj | (a, b) FROM tab | n-distinct: built |
someobj | (a, b) FROM tab | func-dependencies: built |
someobj | (a, b) FROM tab | mcv: built |
sttshoge | (a, b) FROM hoge | n-distinct: required |
sttshoge | (a, b) FROM hoge | func-dependencies:required|
sttscross| (a, b) FROM t1,t2| n-distinct: required |

My opinion is below:

For 1., Agreed. I will remove it on the next patch.
For 2., I feel the design is not beautiful so I'd like to change it.
The reasons are:

- I think that even if we expected the number of types increasing two times,
each type would be better to put as columns, not lines.
Repeating items (the stats name and definition) should be removed.
It's okay there are many columns in the future like "\df+" because we can
use "\x" mode to display if we need it.

- The type column has two kinds of data, the one is stats type and another
is status. We know the word "One fact in One place" for data modeling in
the RDBMS world so it would be better to divide it.
I'd like to suggest the bellow design of the view.

statname | definition | n-distinct | func-dependencies | mcv |
----------+------------------+------------+-------------------+-------|
someobj | (a, b) FROM tab | built | built | built |
sttshoge | (a, b) FROM hoge | required | required | |
sttscross| (a, b) FROM t1,t2| required | | |

Any thoughts?

Thanks,
Tatsuro Yamada

#29Michael Paquier
michael@paquier.xyz
In reply to: Tatsuro Yamada (#28)
Re: list of extended statistics on psql

On Thu, Sep 03, 2020 at 08:45:17AM +0900, Tatsuro Yamada wrote:

I try to summarize the discussion so far.

Could you provide at least a rebased version of the patch? The CF bot
is complaning here.
--
Michael

#30Michael Paquier
michael@paquier.xyz
In reply to: Michael Paquier (#29)
Re: list of extended statistics on psql

On Thu, Sep 17, 2020 at 02:55:31PM +0900, Michael Paquier wrote:

Could you provide at least a rebased version of the patch? The CF bot
is complaning here.

Not seeing this answered after two weeks, I have marked the patch as
RwF for now.
--
Michael

#31Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Michael Paquier (#30)
1 attachment(s)
Re: [spam] Re: list of extended statistics on psql

Hi Michael-san and Hackers,

On 2020/09/30 15:19, Michael Paquier wrote:

On Thu, Sep 17, 2020 at 02:55:31PM +0900, Michael Paquier wrote:

Could you provide at least a rebased version of the patch? The CF bot
is complaning here.

Not seeing this answered after two weeks, I have marked the patch as
RwF for now.
--
Michael

Sorry for the delayed reply.

I re-based the patch on the current head and did some
refactoring.
I think the size of extended stats are not useful for DBA.
Should I remove it?

Changes:
========
- Use a keyword "defined" instead of "not built"
- Use COALESCE function for size for extended stats

Results of \dX and \dX+:
========================
postgres=# \dX
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv
-------------+-----------+-----------------+------------+--------------+---------
public | hoge1_ext | a, b FROM hoge1 | defined | defined | defined
hoge1schema | hoge1_ext | a, b FROM hoge1 | built | built | built
(2 rows)

postgres=# \dX+
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size
-------------+-----------+-----------------+------------+--------------+---------+--------+--------+--------
public | hoge1_ext | a, b FROM hoge1 | defined | defined | defined | 0 | 0 | 0
hoge1schema | hoge1_ext | a, b FROM hoge1 | built | built | built | 13 | 40 | 6126
(2 rows)

Query of \dX+:
==============
SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxname AS "Name",
pg_catalog.format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')
FROM pg_catalog.unnest(es.stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a
ON (es.stxrelid = a.attrelid
AND a.attnum = s.attnum
AND NOT a.attisdropped)),
es.stxrelid::regclass) AS "Definition",
CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built'
WHEN 'd' = any(stxkind) THEN 'defined'
END AS "N_distinct",
CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built'
WHEN 'f' = any(stxkind) THEN 'defined'
END AS "Dependencies",
CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built'
WHEN 'm' = any(stxkind) THEN 'defined'
END AS "Mcv",
COALESCE(pg_catalog.length(stxdndistinct), 0) AS "N_size",
COALESCE(pg_catalog.length(stxddependencies), 0) AS "D_size",
COALESCE(pg_catalog.length(stxdmcv), 0) AS "M_size"
FROM pg_catalog.pg_statistic_ext es
LEFT JOIN pg_catalog.pg_statistic_ext_data esd
ON es.oid = esd.stxoid
INNER JOIN pg_catalog.pg_class c
ON es.stxrelid = c.oid
ORDER BY 1, 2;

Regards,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r4.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r4.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..e0ca32d34b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,98 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCOALESCE(pg_catalog.length(stxdndistinct), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(stxddependencies), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(stxdmcv), 0) AS \"%s\"",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..ec6edaf174 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,71 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(5 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ public | stts_1    | a, b FROM t1               |            | built        |         |      0 |     40 |      0
+ public | stts_2    | a, b FROM t1               | built      | built        |         |     13 |     40 |      0
+ public | stts_3    | a, b FROM t1               | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined |      0 |      0 |      0
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+(5 rows)
+
+\dX+ stts_?
+                                   List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+--------+--------------+------------+--------------+---------+--------+--------+--------
+ public | stts_1 | a, b FROM t1 |            | built        |         |      0 |     40 |      0
+ public | stts_2 | a, b FROM t1 | built      | built        |         |     13 |     40 |      0
+ public | stts_3 | a, b FROM t1 | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined |      0 |      0 |      0
+(4 rows)
+
+\dX+ *hoge
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..7f8aeaea8d 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,26 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
#32Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Michael Paquier (#30)
1 attachment(s)
Re: list of extended statistics on psql

Hi Michael-san and Hackers,

On 2020/09/30 15:19, Michael Paquier wrote:

On Thu, Sep 17, 2020 at 02:55:31PM +0900, Michael Paquier wrote:

Could you provide at least a rebased version of the patch? The CF bot
is complaning here.

Not seeing this answered after two weeks, I have marked the patch as
RwF for now.
--
Michael

Sorry for the delayed reply.

I re-based the patch on the current head and did some
refactoring.
I think the size of extended stats are not useful for DBA.
Should I remove it?

Changes:
========
- Use a keyword "defined" instead of "not built"
- Use COALESCE function for size for extended stats

Results of \dX and \dX+:
========================
postgres=# \dX
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv
-------------+-----------+-----------------+------------+--------------+---------
public | hoge1_ext | a, b FROM hoge1 | defined | defined | defined
hoge1schema | hoge1_ext | a, b FROM hoge1 | built | built | built
(2 rows)

postgres=# \dX+
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size
-------------+-----------+-----------------+------------+--------------+---------+--------+--------+--------
public | hoge1_ext | a, b FROM hoge1 | defined | defined | defined | 0 | 0 | 0
hoge1schema | hoge1_ext | a, b FROM hoge1 | built | built | built | 13 | 40 | 6126
(2 rows)

Query of \dX+:
==============
SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxname AS "Name",
pg_catalog.format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')
FROM pg_catalog.unnest(es.stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a
ON (es.stxrelid = a.attrelid
AND a.attnum = s.attnum
AND NOT a.attisdropped)),
es.stxrelid::regclass) AS "Definition",
CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built'
WHEN 'd' = any(stxkind) THEN 'defined'
END AS "N_distinct",
CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built'
WHEN 'f' = any(stxkind) THEN 'defined'
END AS "Dependencies",
CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built'
WHEN 'm' = any(stxkind) THEN 'defined'
END AS "Mcv",
COALESCE(pg_catalog.length(stxdndistinct), 0) AS "N_size",
COALESCE(pg_catalog.length(stxddependencies), 0) AS "D_size",
COALESCE(pg_catalog.length(stxdmcv), 0) AS "M_size"
FROM pg_catalog.pg_statistic_ext es
LEFT JOIN pg_catalog.pg_statistic_ext_data esd
ON es.oid = esd.stxoid
INNER JOIN pg_catalog.pg_class c
ON es.stxrelid = c.oid
ORDER BY 1, 2;

Regards,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r4.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r4.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..e0ca32d34b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,98 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "stxnamespace::pg_catalog.regnamespace AS \"%s\", \n"
+					  "stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCOALESCE(pg_catalog.length(stxdndistinct), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(stxddependencies), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(stxdmcv), 0) AS \"%s\"",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..ec6edaf174 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,71 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(5 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ public | stts_1    | a, b FROM t1               |            | built        |         |      0 |     40 |      0
+ public | stts_2    | a, b FROM t1               | built      | built        |         |     13 |     40 |      0
+ public | stts_3    | a, b FROM t1               | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined |      0 |      0 |      0
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+(5 rows)
+
+\dX+ stts_?
+                                   List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+--------+--------------+------------+--------------+---------+--------+--------+--------
+ public | stts_1 | a, b FROM t1 |            | built        |         |      0 |     40 |      0
+ public | stts_2 | a, b FROM t1 | built      | built        |         |     13 |     40 |      0
+ public | stts_3 | a, b FROM t1 | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined |      0 |      0 |      0
+(4 rows)
+
+\dX+ *hoge
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+(1 row)
+
+drop table t1, t2, hoge;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..7f8aeaea8d 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,26 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;

#33Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#32)
1 attachment(s)
Re: list of extended statistics on psql

Hi,

Results of \dX and \dX+:
========================
postgres=# \dX
��������������������������� List of extended statistics
��� Schema��� |�� Name��� |�� Definition��� | N_distinct | Dependencies |�� Mcv
-------------+-----------+-----------------+------------+--------------+---------
� public����� | hoge1_ext | a, b FROM hoge1 | defined��� | defined����� | defined
� hoge1schema | hoge1_ext | a, b FROM hoge1 | built����� | built������� | built
(2 rows)

I used "Order by 1, 2" on the query but I realized the ordering of
result was wrong so I fixed on the attached patch.
Please fined the patch file. :-D

Regards,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r5.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r5.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..484f011792 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,98 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCOALESCE(pg_catalog.length(esd.stxdndistinct), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(esd.stxddependencies), 0) AS \"%s\", \n"
+						  "COALESCE(pg_catalog.length(esd.stxdmcv), 0) AS \"%s\"",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..f30df047e1 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,80 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama on col1, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    | defined      | defined
+(7 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined |      0 |      0 |      0
+ public | stts_1    | a, b FROM t1               |            | built        |         |      0 |     40 |      0
+ public | stts_2    | a, b FROM t1               | built      | built        |         |     13 |     40 |      0
+ public | stts_3    | a, b FROM t1               | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined |      0 |      0 |      0
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    | defined      | defined |      0 |      0 |      0
+(7 rows)
+
+\dX+ stts_?
+                                   List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+--------+--------------+------------+--------------+---------+--------+--------+--------
+ public | stts_1 | a, b FROM t1 |            | built        |         |      0 |     40 |      0
+ public | stts_2 | a, b FROM t1 | built      | built        |         |     13 |     40 |      0
+ public | stts_3 | a, b FROM t1 | built      | built        | built   |     13 |     40 |   6126
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined |      0 |      0 |      0
+(4 rows)
+
+\dX+ *hoge
+                                           List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size | D_size | M_size 
+--------+-----------+----------------------------+------------+--------------+---------+--------+--------+--------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined |      0 |      0 |      0
+(1 row)
+
+drop table t1, t2, hoge;
+drop schema foo, yama;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..747e23a02a 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,32 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama on col1, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
+drop schema foo, yama;
#34Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Tatsuro Yamada (#32)
Re: list of extended statistics on psql

On Wed, Oct 28, 2020 at 03:07:56PM +0900, Tatsuro Yamada wrote:

Hi Michael-san and Hackers,

On 2020/09/30 15:19, Michael Paquier wrote:

On Thu, Sep 17, 2020 at 02:55:31PM +0900, Michael Paquier wrote:

Could you provide at least a rebased version of the patch? The CF bot
is complaning here.

Not seeing this answered after two weeks, I have marked the patch as
RwF for now.
--
Michael

Sorry for the delayed reply.

I re-based the patch on the current head and did some
refactoring.
I think the size of extended stats are not useful for DBA.
Should I remove it?

I think it's an interesting / useful information, I'd keep it (in the
\dX+ output only, of course). But I think it needs to print the size
similarly to \d+, i.e. using pg_size_pretty - that'll include the unit
and make it more readable for large stats.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#35Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Tatsuro Yamada (#33)
Re: list of extended statistics on psql

On Wed, Oct 28, 2020 at 04:20:25PM +0900, Tatsuro Yamada wrote:

Hi,

Results of \dX and \dX+:
========================
postgres=# \dX
��������������������������� List of extended statistics
��� Schema��� |�� Name��� |�� Definition��� | N_distinct | Dependencies |�� Mcv
-------------+-----------+-----------------+------------+--------------+---------
� public����� | hoge1_ext | a, b FROM hoge1 | defined��� | defined����� | defined
� hoge1schema | hoge1_ext | a, b FROM hoge1 | built����� | built������� | built
(2 rows)

I used "Order by 1, 2" on the query but I realized the ordering of
result was wrong so I fixed on the attached patch.
Please fined the patch file. :-D

Thanks. I'll take a look at the beginning of the 2020-11 commitfest, and
I hope to get this committed.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#36Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#35)
Re: list of extended statistics on psql

Hi Tomas,

On 2020/10/29 4:07, Tomas Vondra wrote:

On Wed, Oct 28, 2020 at 04:20:25PM +0900, Tatsuro Yamada wrote:

Hi,

Results of \dX and \dX+:
========================
postgres=# \dX
                            List of extended statistics
    Schema    |   Name    |   Definition    | N_distinct | Dependencies |   Mcv
-------------+-----------+-----------------+------------+--------------+---------
  public      | hoge1_ext | a, b FROM hoge1 | defined    | defined      | defined
  hoge1schema | hoge1_ext | a, b FROM hoge1 | built      | built        | built
(2 rows)

I used "Order by 1, 2" on the query but I realized the ordering of
result was wrong so I fixed on the attached patch.
Please find the patch file. :-D

Thanks. I'll take a look at the beginning of the 2020-11 commitfest, and
I hope to get this committed.

Thanks for your reply and I'm glad to hear that.

I'm going to revise the patch as possible to get this committed on
the next commitfest.

Regards,
Tatsuro Yamada

#37Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#34)
1 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

On 2020/10/29 4:06, Tomas Vondra wrote:

On Wed, Oct 28, 2020 at 03:07:56PM +0900, Tatsuro Yamada wrote:

Hi Michael-san and Hackers,

On 2020/09/30 15:19, Michael Paquier wrote:

On Thu, Sep 17, 2020 at 02:55:31PM +0900, Michael Paquier wrote:

Could you provide at least a rebased version of the patch?  The CF bot
is complaning here.

Not seeing this answered after two weeks, I have marked the patch as
RwF for now.
--
Michael

Sorry for the delayed reply.

I re-based the patch on the current head and did some
refactoring.
I think the size of extended stats are not useful for DBA.
Should I remove it?

I think it's an interesting / useful information, I'd keep it (in the
\dX+ output only, of course). But I think it needs to print the size
similarly to \d+, i.e. using pg_size_pretty - that'll include the unit
and make it more readable for large stats.

Thanks for your comment.
I addressed it, so I keep the size of extended stats with the unit.

Changes:
========
- Use pg_size_pretty to show the size of extended stats by \dX+

Result of \dX+:
===============
Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_Size | D_Size | M_Size
-------------+------------+-----------------+------------+--------------+---------+----------+----------+------------
hoge1schema | hoge1_ext | a, b FROM hoge1 | built | built | built | 13 bytes | 40 bytes | 6126 bytes
public | hoge1_ext1 | a, b FROM hoge1 | defined | defined | defined | 0 bytes | 0 bytes | 0 bytes
public | hoge1_ext2 | a, b FROM hoge1 | defined | | | 0 bytes | |
(3 rows)

Please find the attached patch.

Regards,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r6.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r6.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..1807324542 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..701f2dbe59 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,80 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined
+(7 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                               List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+-----------+----------------------------+------------+--------------+---------+----------+----------+------------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_1    | a, b FROM t1               |            | built        |         |          | 40 bytes | 
+ public | stts_2    | a, b FROM t1               | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined | 0 bytes  |          | 0 bytes
+(7 rows)
+
+\dX+ stts_?
+                                       List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+--------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM t1 |            | built        |         |          | 40 bytes | 
+ public | stts_2 | a, b FROM t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+(4 rows)
+
+\dX+ *hoge
+                                             List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+----------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
+drop table t1, t2, hoge;
+drop schema foo, yama;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..dc9f4b2159 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,32 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
+drop schema foo, yama;
#38Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#37)
1 attachment(s)
Re: list of extended statistics on psql

Hi,

I addressed it, so I keep the size of extended stats with the unit.

Changes:
========
  - Use pg_size_pretty to show the size of extended stats by \dX+

I rebased the patch on the head and also added tab-completion.
Any feedback is welcome.

Preparing for tests:
===========
create table t1 (a int, b int);
create statistics stts_1 (dependencies) on a, b from t1;
create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;

create table t2 (a int, b int, c int);
create statistics stts_4 on b, c from t2;

create table hoge (col1 int, col2 int, col3 int);
create statistics stts_hoge on col1, col2, col3 from hoge;

create schema foo;
create schema yama;
create statistics foo.stts_foo on col1, col2 from hoge;
create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;

insert into t1 select i,i from generate_series(1,100) i;
analyze t1;

Result of \dX:
==============
postgres=# \dX
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv
--------+-----------+----------------------------+------------+--------------+---------
foo | stts_foo | col1, col2 FROM hoge | defined | defined | defined
public | stts_1 | a, b FROM t1 | | built |
public | stts_2 | a, b FROM t1 | built | built |
public | stts_3 | a, b FROM t1 | built | built | built
public | stts_4 | b, c FROM t2 | defined | defined | defined
public | stts_hoge | col1, col2, col3 FROM hoge | defined | defined | defined
yama | stts_yama | col1, col3 FROM hoge | defined | | defined
(7 rows)

Result of \dX+:
===============
postgres=# \dX+
List of extended statistics
Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size
--------+-----------+----------------------------+------------+--------------+---------+----------+----------+------------
foo | stts_foo | col1, col2 FROM hoge | defined | defined | defined | 0 bytes | 0 bytes | 0 bytes
public | stts_1 | a, b FROM t1 | | built | | | 40 bytes |
public | stts_2 | a, b FROM t1 | built | built | | 13 bytes | 40 bytes |
public | stts_3 | a, b FROM t1 | built | built | built | 13 bytes | 40 bytes | 6126 bytes
public | stts_4 | b, c FROM t2 | defined | defined | defined | 0 bytes | 0 bytes | 0 bytes
public | stts_hoge | col1, col2, col3 FROM hoge | defined | defined | defined | 0 bytes | 0 bytes | 0 bytes
yama | stts_yama | col1, col3 FROM hoge | defined | | defined | 0 bytes | | 0 bytes
(7 rows)

Results of Tab-completion:
===============
postgres=# \dX <Tab>
foo. pg_toast. stts_2 stts_hoge
information_schema. public. stts_3 yama.
pg_catalog. stts_1 stts_4

postgres=# \dX+ <Tab>
foo. pg_toast. stts_2 stts_hoge
information_schema. public. stts_3 yama.
pg_catalog. stts_1 stts_4

Regards,
Tatsuro Yamada

Attachments:

add_list_extended_stats_for_psql_by_dX_and_dXplus_r7.patchtext/plain; charset=UTF-8; name=add_list_extended_stats_for_psql_by_dX_and_dXplus_r7.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..1807324542 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5238a960f7..30a8ada7d2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3851,6 +3851,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..701f2dbe59 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,80 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined
+(7 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                               List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+-----------+----------------------------+------------+--------------+---------+----------+----------+------------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_1    | a, b FROM t1               |            | built        |         |          | 40 bytes | 
+ public | stts_2    | a, b FROM t1               | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined | 0 bytes  |          | 0 bytes
+(7 rows)
+
+\dX+ stts_?
+                                       List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+--------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM t1 |            | built        |         |          | 40 bytes | 
+ public | stts_2 | a, b FROM t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+(4 rows)
+
+\dX+ *hoge
+                                             List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+----------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
+drop table t1, t2, hoge;
+drop schema foo, yama;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..dc9f4b2159 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,32 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
+drop schema foo, yama;
#39Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#38)
2 attachment(s)
Re: list of extended statistics on psql

Hi,

I took a look at this today, and I think the code is ready, but the
regression test needs a bit more work:

1) It's probably better to use somewhat more specific names for the
objects, especially when created in public schema. It decreases the
chance of a collision with other tests (which may be hard to notice
because of timing). I suggest we use "stts_" prefix or something like
that, per the attached 0002 patch. (0001 is just the v7 patch)

2) The test is failing intermittently because it's executed in parallel
with stats_ext test, which is also creating extended statistics. So
depending on the timing the \dX may list some of the stats_ext stuff.
I'm not sure what to do about this. Either this part needs to be moved
to a separate test executed in a different group, or maybe we should
simply move it to stats_ext.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

0001-v7-20201108.patchtext/x-patch; charset=UTF-8; name=0001-v7-20201108.patchDownload
From 1a7e99c931c0b3ed5560abd3ff656412f8b353f1 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 8 Nov 2020 21:53:54 +0100
Subject: [PATCH 1/2] v7

---
 doc/src/sgml/ref/psql-ref.sgml     |  14 ++++
 src/bin/psql/command.c             |   3 +
 src/bin/psql/describe.c            | 100 +++++++++++++++++++++++++++++
 src/bin/psql/describe.h            |   3 +
 src/bin/psql/help.c                |   1 +
 src/bin/psql/tab-complete.c        |   4 +-
 src/test/regress/expected/psql.out |  77 ++++++++++++++++++++++
 src/test/regress/sql/psql.sql      |  29 +++++++++
 8 files changed, 230 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..1807324542 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5238a960f7..30a8ada7d2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3851,6 +3851,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..701f2dbe59 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5072,3 +5072,80 @@ List of access methods
  hash  | uuid_ops        | uuid                 | uuid                  |      2 | uuid_hash_extended
 (5 rows)
 
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+\dX
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined
+ public | stts_1    | a, b FROM t1               |            | built        | 
+ public | stts_2    | a, b FROM t1               | built      | built        | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined
+(7 rows)
+
+\dX stts_?
+                     List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
+--------+--------+--------------+------------+--------------+---------
+ public | stts_1 | a, b FROM t1 |            | built        | 
+ public | stts_2 | a, b FROM t1 | built      | built        | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *hoge
+                              List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
+--------+-----------+----------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                               List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+-----------+----------------------------+------------+--------------+---------+----------+----------+------------
+ foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_1    | a, b FROM t1               |            | built        |         |          | 40 bytes | 
+ public | stts_2    | a, b FROM t1               | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3    | a, b FROM t1               | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4    | b, c FROM t2               | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined | 0 bytes  |          | 0 bytes
+(7 rows)
+
+\dX+ stts_?
+                                       List of extended statistics
+ Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+--------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM t1 |            | built        |         |          | 40 bytes | 
+ public | stts_2 | a, b FROM t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+(4 rows)
+
+\dX+ *hoge
+                                             List of extended statistics
+ Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+----------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
+drop table t1, t2, hoge;
+drop schema foo, yama;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 47b28d2a07..dc9f4b2159 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1225,3 +1225,32 @@ drop role regress_partitioning_role;
 \dAo * pg_catalog.jsonb_path_ops
 \dAp+ btree float_ops
 \dAp * pg_catalog.uuid_ops
+
+-- check printing info about extended statistics
+create table t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+
+create table t2 (a int, b int, c int);
+create statistics stts_4 on b, c from t2;
+
+create table hoge (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from hoge;
+
+create schema foo;
+create schema yama;
+create statistics foo.stts_foo on col1, col2 from hoge;
+create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+
+insert into t1 select i,i from generate_series(1,100) i;
+analyze t1;
+
+\dX
+\dX stts_?
+\dX *hoge
+\dX+
+\dX+ stts_?
+\dX+ *hoge
+drop table t1, t2, hoge;
+drop schema foo, yama;
-- 
2.26.2

0002-rename-objects-in-regression-test-20201108.patchtext/x-patch; charset=UTF-8; name=0002-rename-objects-in-regression-test-20201108.patchDownload
From 9a6f15a7d531afa28f8cd8f67edbe301a8ea7ed5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 8 Nov 2020 22:37:29 +0100
Subject: [PATCH 2/2] rename objects in regression test

---
 src/test/regress/expected/psql.out | 120 ++++++++++++++---------------
 src/test/regress/sql/psql.sql      |  36 ++++-----
 2 files changed, 78 insertions(+), 78 deletions(-)

diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 701f2dbe59..27c73445d5 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5073,79 +5073,79 @@ List of access methods
 (5 rows)
 
 -- check printing info about extended statistics
-create table t1 (a int, b int);
-create statistics stts_1 (dependencies) on a, b from t1;
-create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
-create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
-create table t2 (a int, b int, c int);
-create statistics stts_4 on b, c from t2;
-create table hoge (col1 int, col2 int, col3 int);
-create statistics stts_hoge on col1, col2, col3 from hoge;
-create schema foo;
-create schema yama;
-create statistics foo.stts_foo on col1, col2 from hoge;
-create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
-insert into t1 select i,i from generate_series(1,100) i;
-analyze t1;
+create table stts_t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from stts_t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from stts_t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (ndistinct, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
 \dX
-                              List of extended statistics
- Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
---------+-----------+----------------------------+------------+--------------+---------
- foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined
- public | stts_1    | a, b FROM t1               |            | built        | 
- public | stts_2    | a, b FROM t1               | built      | built        | 
- public | stts_3    | a, b FROM t1               | built      | built        | built
- public | stts_4    | b, c FROM t2               | defined    | defined      | defined
- public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
- yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined
+                                List of extended statistics
+ Schema  |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   
+---------+-----------+-------------------------------+------------+--------------+---------
+ public  | stts_1    | a, b FROM stts_t1             |            | built        | 
+ public  | stts_2    | a, b FROM stts_t1             | built      | built        | 
+ public  | stts_3    | a, b FROM stts_t1             | built      | built        | built
+ public  | stts_4    | b, c FROM stts_t2             | defined    | defined      | defined
+ public  | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined
+ stts_s1 | stts_foo  | col1, col2 FROM stts_t3       | defined    | defined      | defined
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3       | defined    |              | defined
 (7 rows)
 
 \dX stts_?
-                     List of extended statistics
- Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   
---------+--------+--------------+------------+--------------+---------
- public | stts_1 | a, b FROM t1 |            | built        | 
- public | stts_2 | a, b FROM t1 | built      | built        | 
- public | stts_3 | a, b FROM t1 | built      | built        | built
- public | stts_4 | b, c FROM t2 | defined    | defined      | defined
+                        List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   
+--------+--------+-------------------+------------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 |            | built        | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined
 (4 rows)
 
-\dX *hoge
-                              List of extended statistics
- Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   
---------+-----------+----------------------------+------------+--------------+---------
- public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   
+--------+-----------+-------------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined
 (1 row)
 
 \dX+
-                                               List of extended statistics
- Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
---------+-----------+----------------------------+------------+--------------+---------+----------+----------+------------
- foo    | stts_foo  | col1, col2 FROM hoge       | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
- public | stts_1    | a, b FROM t1               |            | built        |         |          | 40 bytes | 
- public | stts_2    | a, b FROM t1               | built      | built        |         | 13 bytes | 40 bytes | 
- public | stts_3    | a, b FROM t1               | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
- public | stts_4    | b, c FROM t2               | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
- public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
- yama   | stts_yama | col1, col3 FROM hoge       | defined    |              | defined | 0 bytes  |          | 0 bytes
+                                                 List of extended statistics
+ Schema  |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+---------+-----------+-------------------------------+------------+--------------+---------+----------+----------+------------
+ public  | stts_1    | a, b FROM stts_t1             |            | built        |         |          | 40 bytes | 
+ public  | stts_2    | a, b FROM stts_t1             | built      | built        |         | 13 bytes | 40 bytes | 
+ public  | stts_3    | a, b FROM stts_t1             | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public  | stts_4    | b, c FROM stts_t2             | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ public  | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ stts_s1 | stts_foo  | col1, col2 FROM stts_t3       | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3       | defined    |              | defined | 0 bytes  |          | 0 bytes
 (7 rows)
 
 \dX+ stts_?
-                                       List of extended statistics
- Schema |  Name  |  Definition  | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
---------+--------+--------------+------------+--------------+---------+----------+----------+------------
- public | stts_1 | a, b FROM t1 |            | built        |         |          | 40 bytes | 
- public | stts_2 | a, b FROM t1 | built      | built        |         | 13 bytes | 40 bytes | 
- public | stts_3 | a, b FROM t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
- public | stts_4 | b, c FROM t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+                                         List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+-------------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM stts_t1 |            | built        |         |          | 40 bytes | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
 (4 rows)
 
-\dX+ *hoge
-                                             List of extended statistics
- Schema |   Name    |         Definition         | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
---------+-----------+----------------------------+------------+--------------+---------+---------+---------+---------
- public | stts_hoge | col1, col2, col3 FROM hoge | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+\dX+ *stts_hoge
+                                              List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+-------------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
 (1 row)
 
-drop table t1, t2, hoge;
-drop schema foo, yama;
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2;
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index dc9f4b2159..5ebfe184a4 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1227,30 +1227,30 @@ drop role regress_partitioning_role;
 \dAp * pg_catalog.uuid_ops
 
 -- check printing info about extended statistics
-create table t1 (a int, b int);
-create statistics stts_1 (dependencies) on a, b from t1;
-create statistics stts_2 (dependencies, ndistinct) on a, b from t1;
-create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from t1;
+create table stts_t1 (a int, b int);
+create statistics stts_1 (dependencies) on a, b from stts_t1;
+create statistics stts_2 (dependencies, ndistinct) on a, b from stts_t1;
+create statistics stts_3 (dependencies, ndistinct, mcv) on a, b from stts_t1;
 
-create table t2 (a int, b int, c int);
-create statistics stts_4 on b, c from t2;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
 
-create table hoge (col1 int, col2 int, col3 int);
-create statistics stts_hoge on col1, col2, col3 from hoge;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
 
-create schema foo;
-create schema yama;
-create statistics foo.stts_foo on col1, col2 from hoge;
-create statistics yama.stts_yama (ndistinct, mcv) on col1, col3 from hoge;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (ndistinct, mcv) on col1, col3 from stts_t3;
 
-insert into t1 select i,i from generate_series(1,100) i;
-analyze t1;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
 
 \dX
 \dX stts_?
-\dX *hoge
+\dX *stts_hoge
 \dX+
 \dX+ stts_?
-\dX+ *hoge
-drop table t1, t2, hoge;
-drop schema foo, yama;
+\dX+ *stts_hoge
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2;
-- 
2.26.2

#40Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#39)
2 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

I took a look at this today, and I think the code is ready, but the
regression test needs a bit more work:

Thanks for taking your time. :-D

1) It's probably better to use somewhat more specific names for the
objects, especially when created in public schema. It decreases the
chance of a collision with other tests (which may be hard to notice
because of timing). I suggest we use "stts_" prefix or something like
that, per the attached 0002 patch. (0001 is just the v7 patch)

I agree with your comment. Thanks.

2) The test is failing intermittently because it's executed in parallel
with stats_ext test, which is also creating extended statistics. So
depending on the timing the \dX may list some of the stats_ext stuff.
I'm not sure what to do about this. Either this part needs to be moved
to a separate test executed in a different group, or maybe we should
simply move it to stats_ext.

I thought all tests related to meta-commands exist in psql.sql, but I
realize it's not true. For example, the test of \dRp does not exist in
psql.sql. Therefore, I moved the regression test of \dX to stats_ext.sql
to avoid the test failed in parallel.

Attached patches is following:
- 0001-v8-Add-dX-command-on-psql.patch
- 0002-Add-regression-test-of-dX-to-stats_ext.sql.patch

However, I feel the test of \dX is not elegant, so I'm going to try
creating another one since it would be better to be aware of the context
of existing extended stats tests.

Regards,
Tatsuro Yamada

Attachments:

0002-Add-regression-test-of-dX-to-stats_ext.sql.patchtext/plain; charset=UTF-8; name=0002-Add-regression-test-of-dX-to-stats_ext.sql.patchDownload
From 11c088cb43cdb13e445e246d0529d5721cf3bb10 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 10 Nov 2020 11:39:14 +0900
Subject: [PATCH] Add regression test of \dX to stats_ext.sql

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 4c3edd213f..0ec4e24960 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1550,6 +1550,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | N_distinct | Dependencies |   Mcv   
+----------+------------------------+--------------------------------------+------------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |            | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |            |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |            |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |            |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built      |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built      | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built      | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined    | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined    | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined    | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |            | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |            |              | built
+(12 rows)
+
+\dX stts_?
+                        List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   
+--------+--------+-------------------+------------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built      |              | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   
+--------+-----------+-------------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                                            List of extended statistics
+  Schema  |          Name          |              Definition              | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size   |   M_size   
+----------+------------------------+--------------------------------------+------------+--------------+---------+----------+-----------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |            | built        |         |          | 106 bytes | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |            |              | built   |          |           | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |            |              | built   |          |           | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |            |              | built   |          |           | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built      |              |         | 13 bytes |           | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built      | built        |         | 13 bytes | 40 bytes  | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built      | built        | built   | 13 bytes | 40 bytes  | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |            | defined      | defined |          | 0 bytes   | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |            |              | built   |          |           | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                         List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+-------------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM stts_t1 | built      |              |         | 13 bytes |          | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                              List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+-------------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                           List of extended statistics
+ Schema  |   Name    |       Definition        | N_distinct | Dependencies |   Mcv   | N_size | D_size  | M_size  
+---------+-----------+-------------------------+------------+--------------+---------+--------+---------+---------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |            | defined      | defined |        | 0 bytes | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 9781e590a3..2b90471a4b 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -833,6 +833,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

0001-v8-Add-dX-command-on-psql.patchtext/plain; charset=UTF-8; name=0001-v8-Add-dX-command-on-psql.patchDownload
From 6de4684532f7cdf5451f4969ab83514fa5bb19d3 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Mon, 9 Nov 2020 11:25:14 +0900
Subject: [PATCH] Add \dX command on psql

---
 doc/src/sgml/ref/psql-ref.sgml |  14 ++++++
 src/bin/psql/command.c         |   3 ++
 src/bin/psql/describe.c        | 100 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 ++
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..1807324542 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5238a960f7..30a8ada7d2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3851,6 +3851,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

#41Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#40)
1 attachment(s)
Re: list of extended statistics on psql

Hi,

2) The test is failing intermittently because it's executed in parallel
with stats_ext test, which is also creating extended statistics. So
depending on the timing the \dX may list some of the stats_ext stuff.
I'm not sure what to do about this. Either this part needs to be moved
to a separate test executed in a different group, or maybe we should
simply move it to stats_ext.

I thought all tests related to meta-commands exist in psql.sql, but I
realize it's not true. For example, the test of \dRp does not exist in
psql.sql. Therefore, I moved the regression test of \dX to stats_ext.sql
to avoid the test failed in parallel.

Attached patches is following:
 - 0001-v8-Add-dX-command-on-psql.patch
 - 0002-Add-regression-test-of-dX-to-stats_ext.sql.patch

However, I feel the test of \dX is not elegant, so I'm going to try
creating another one since it would be better to be aware of the context
of existing extended stats tests.

I tried to create another version of the regression test (0003).
"\dX" was added after ANALYZE command or SELECT... from pg_statistic_ext.

Please find the attached file:
- 0003-Add-regression-test-of-dX-to-stats_ext.sql-another-ver

Both regression tests 0002 and 0003 are okay for me, I think.
Could you choose one?

Regards,
Tatsuro Yamada

Attachments:

0003-Add-regression-test-of-dX-to-stats_ext.sql-another-ver.patchtext/plain; charset=UTF-8; name=0003-Add-regression-test-of-dX-to-stats_ext.sql-another-ver.patchDownload
From b22ebf34fc09e246f8d4cf408f76a6753f3d6bcb Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 10 Nov 2020 16:47:45 +0900
Subject: [PATCH] Add regression test of \dX to stats_ext.sql (another version)

---
 src/test/regress/sql/stats_ext.sql | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 9781e590a3..c590dcc90a 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -49,6 +49,7 @@ CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1;
 
 -- Let's also verify the pg_get_statisticsobjdef output looks sane.
 SELECT pg_get_statisticsobjdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats';
+\dX
 
 DROP STATISTICS regress_schema_2.ab1_a_b_stats;
 
@@ -60,9 +61,10 @@ ALTER TABLE ab1 DROP COLUMN a;
 \d ab1
 -- Ensure statistics are dropped when table is
 SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
+\dX
 DROP TABLE ab1;
 SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
-
+\dX
 -- Ensure things work sanely with SET STATISTICS 0
 CREATE TABLE ab1 (a INTEGER, b INTEGER);
 ALTER TABLE ab1 ALTER a SET STATISTICS 0;
@@ -73,13 +75,16 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1;
 -- setting statistics target 0 skips the statistics, without printing any message, so check catalog
 ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
 \d ab1
+\dX+ ab1_a_b_stats
 ANALYZE ab1;
 SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxname = 'ab1_a_b_stats'
    AND d.stxoid = s.oid;
+\dX+ ab1_a_b_stats
 ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
 \d+ ab1
+\dX+ ab1_a_b_stats
 -- partial analyze doesn't build stats either
 ANALYZE ab1 (a);
 ANALYZE ab1;
@@ -93,6 +98,7 @@ CREATE TABLE ab1c () INHERITS (ab1);
 INSERT INTO ab1 VALUES (1,1);
 CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
 ANALYZE ab1;
+\dX+ ab1_a_b_stats
 DROP TABLE ab1 CASCADE;
 
 -- Verify supported object types for extended statistics
@@ -171,6 +177,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+ s10
 
 -- minor improvement, make sure the ctid does not break the matching
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b');
@@ -202,6 +209,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+ s10
 
 -- correct estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -220,6 +228,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+
 
 -- dropping the statistics results in under-estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -261,6 +270,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
 
@@ -274,6 +284,7 @@ INSERT INTO functional_dependencies (a, b, c, filler1)
      SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i);
 
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
 
@@ -335,6 +346,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
+\dx+ func_deps_stat
 
 -- print the detected dependencies
 SELECT dependencies FROM pg_stats_ext WHERE statistics_name = 'func_deps_stat';
@@ -437,6 +449,7 @@ CREATE STATISTICS functional_dependencies_multi_1 (dependencies) ON a, b FROM fu
 CREATE STATISTICS functional_dependencies_multi_2 (dependencies) ON c, d FROM functional_dependencies_multi;
 
 ANALYZE functional_dependencies_multi;
+\dX+ functional_dependencies_multi_*
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0');
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND 0 = b');
@@ -472,6 +485,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b =
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -536,6 +550,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -591,6 +606,7 @@ SELECT d.stxdmcv IS NOT NULL
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dX+ mcv_lists_stats
 
 -- check change of column type resets the MCV statistics
 ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
@@ -598,6 +614,7 @@ ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -629,6 +646,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (0, 1) AN
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL');
 
@@ -650,6 +668,7 @@ SELECT m.*
        pg_mcv_list_items(d.stxdmcv) m
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dX+ mcv_lists_stats
 
 -- 2 distinct combinations with NULL values, all in the MCV list
 TRUNCATE mcv_lists;
@@ -682,6 +701,7 @@ SELECT m.*
        pg_mcv_list_items(d.stxdmcv) m
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dx+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x''');
 
@@ -714,6 +734,7 @@ CREATE STATISTICS mcv_lists_uuid_stats (mcv) ON a, b, c
   FROM mcv_lists_uuid;
 
 ANALYZE mcv_lists_uuid;
+\dX+ mcv_lists_uuid_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc''');
 
@@ -740,6 +761,7 @@ CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c
   FROM mcv_lists_arrays;
 
 ANALYZE mcv_lists_arrays;
+\dX+ mcv_lists_arrays_stats
 
 -- mcv with bool
 CREATE TABLE mcv_lists_bool (
@@ -768,6 +790,7 @@ CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c
   FROM mcv_lists_bool;
 
 ANALYZE mcv_lists_bool;
+\dX+ mcv_lists_bool_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c');
 
@@ -806,6 +829,7 @@ CREATE STATISTICS mcv_lists_multi_1 (mcv) ON a, b FROM mcv_lists_multi;
 CREATE STATISTICS mcv_lists_multi_2 (mcv) ON c, d FROM mcv_lists_multi;
 
 ANALYZE mcv_lists_multi;
+\dX+ mcv_lists_multi_*
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0');
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE c = 0 AND d = 0');
@@ -832,6 +856,7 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 
 ANALYZE tststats.priv_test_tbl;
+\dX+ tststats.priv_test_stats
 
 -- User with no access
 CREATE USER regress_stats_user1;
-- 
2.16.5

#42Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#41)
3 attachment(s)
Re: list of extended statistics on psql

Thanks,

It's better to always post the whole patch series, so that cfbot can
test it properly. Sending just 0003 separately kind breaks that.

Also, 0003 seems to only tweak the .sql file, not the expected output,
and there actually seems to be two places that mistakenly use \dx (so
listing extensions) instead of \dX. I've fixed both issues in the
attached patches.

However, I think the 0002 tests are better/sufficient - I prefer to keep
it compact, not interleaving with the tests testing various other stuff.
So I don't intend to commit 0003, unless there's something that I don't
see for some reason.

The one remaining thing I'm not sure about is naming of the columns with
size of statistics - N_size, D_size and M_size does not seem very clear.
Any clearer naming will however make the tables wider, though :-/

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

0001-Add-dX-command-on-psql-20201115.patchtext/x-patch; charset=UTF-8; name=0001-Add-dX-command-on-psql-20201115.patchDownload
From e552a17a9c74952a002e6dabe8d57c9ab787addb Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Mon, 9 Nov 2020 11:25:14 +0900
Subject: [PATCH 1/3] Add \dX command on psql

---
 doc/src/sgml/ref/psql-ref.sgml |  14 +++++
 src/bin/psql/command.c         |   3 +
 src/bin/psql/describe.c        | 100 +++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 +
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..1807324542 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("N_distinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("Mcv"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("N_size"),
+						  gettext_noop("D_size"),
+						  gettext_noop("M_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5238a960f7..30a8ada7d2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3851,6 +3851,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.26.2

0002-Add-regression-test-of-dX-to-stats_ext.sql-20201115.patchtext/x-patch; charset=UTF-8; name=0002-Add-regression-test-of-dX-to-stats_ext.sql-20201115.patchDownload
From 3daa574b29b77b1d9586e32c1c2a9b27a7e3565d Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 10 Nov 2020 11:39:14 +0900
Subject: [PATCH 2/3] Add regression test of \dX to stats_ext.sql

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 ++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 4c3edd213f..0ec4e24960 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1550,6 +1550,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | N_distinct | Dependencies |   Mcv   
+----------+------------------------+--------------------------------------+------------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |            | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |            |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |            |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |            |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built      |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built      | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built      | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined    | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined    | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined    | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |            | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |            |              | built
+(12 rows)
+
+\dX stts_?
+                        List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   
+--------+--------+-------------------+------------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built      |              | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   
+--------+-----------+-------------------------------+------------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined
+(1 row)
+
+\dX+
+                                                            List of extended statistics
+  Schema  |          Name          |              Definition              | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size   |   M_size   
+----------+------------------------+--------------------------------------+------------+--------------+---------+----------+-----------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |            | built        |         |          | 106 bytes | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |            |              | built   |          |           | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |            |              | built   |          |           | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |            |              | built   |          |           | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built      |              |         | 13 bytes |           | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built      | built        |         | 13 bytes | 40 bytes  | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built      | built        | built   | 13 bytes | 40 bytes  | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined    | defined      | defined | 0 bytes  | 0 bytes   | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |            | defined      | defined |          | 0 bytes   | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |            |              | built   |          |           | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                         List of extended statistics
+ Schema |  Name  |    Definition     | N_distinct | Dependencies |   Mcv   |  N_size  |  D_size  |   M_size   
+--------+--------+-------------------+------------+--------------+---------+----------+----------+------------
+ public | stts_1 | a, b FROM stts_t1 | built      |              |         | 13 bytes |          | 
+ public | stts_2 | a, b FROM stts_t1 | built      | built        |         | 13 bytes | 40 bytes | 
+ public | stts_3 | a, b FROM stts_t1 | built      | built        | built   | 13 bytes | 40 bytes | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined    | defined      | defined | 0 bytes  | 0 bytes  | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                              List of extended statistics
+ Schema |   Name    |          Definition           | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+-----------+-------------------------------+------------+--------------+---------+---------+---------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                           List of extended statistics
+ Schema  |   Name    |       Definition        | N_distinct | Dependencies |   Mcv   | N_size | D_size  | M_size  
+---------+-----------+-------------------------+------------+--------------+---------+--------+---------+---------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |            | defined      | defined |        | 0 bytes | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 9781e590a3..2b90471a4b 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -833,6 +833,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.26.2

0003-Add-regression-test-of-dX-to-stats_ext.sql--20201115.patchtext/x-patch; charset=UTF-8; name=0003-Add-regression-test-of-dX-to-stats_ext.sql--20201115.patchDownload
From 62ef3806a959929c37d0847cffefc8e57e56c235 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 15 Nov 2020 19:00:23 +0100
Subject: [PATCH 3/3] Add regression test of \dX to stats_ext.sql (another
 version)

---
 src/test/regress/expected/stats_ext.out | 181 ++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      |  27 +++-
 2 files changed, 207 insertions(+), 1 deletion(-)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 0ec4e24960..fb10ee2237 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -64,6 +64,13 @@ SELECT pg_get_statisticsobjdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a
  CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1
 (1 row)
 
+\dX
+                              List of extended statistics
+      Schema      |     Name      |  Definition   | N_distinct | Dependencies |   Mcv   
+------------------+---------------+---------------+------------+--------------+---------
+ regress_schema_2 | ab1_a_b_stats | a, b FROM ab1 | defined    | defined      | defined
+(1 row)
+
 DROP STATISTICS regress_schema_2.ab1_a_b_stats;
 -- Ensure statistics are dropped when columns are
 CREATE STATISTICS ab1_b_c_stats ON b, c FROM ab1;
@@ -86,12 +93,25 @@ SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
  ab1_b_c_stats
 (1 row)
 
+\dX
+                         List of extended statistics
+ Schema |     Name      |  Definition   | N_distinct | Dependencies |   Mcv   
+--------+---------------+---------------+------------+--------------+---------
+ public | ab1_b_c_stats | b, c FROM ab1 | defined    | defined      | defined
+(1 row)
+
 DROP TABLE ab1;
 SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
  stxname 
 ---------
 (0 rows)
 
+\dX
+                 List of extended statistics
+ Schema | Name | Definition | N_distinct | Dependencies | Mcv 
+--------+------+------------+------------+--------------+-----
+(0 rows)
+
 -- Ensure things work sanely with SET STATISTICS 0
 CREATE TABLE ab1 (a INTEGER, b INTEGER);
 ALTER TABLE ab1 ALTER a SET STATISTICS 0;
@@ -111,6 +131,13 @@ ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
 Statistics objects:
     "public"."ab1_a_b_stats" (ndistinct, dependencies, mcv) ON a, b FROM ab1; STATISTICS 0
 
+\dX+ ab1_a_b_stats
+                                        List of extended statistics
+ Schema |     Name      |  Definition   | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+---------------+---------------+------------+--------------+---------+---------+---------+---------
+ public | ab1_a_b_stats | a, b FROM ab1 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
 ANALYZE ab1;
 SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
   FROM pg_statistic_ext s, pg_statistic_ext_data d
@@ -121,6 +148,13 @@ SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
  ab1_a_b_stats |               |                  | 
 (1 row)
 
+\dX+ ab1_a_b_stats
+                                        List of extended statistics
+ Schema |     Name      |  Definition   | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+---------------+---------------+------------+--------------+---------+---------+---------+---------
+ public | ab1_a_b_stats | a, b FROM ab1 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
 ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
 \d+ ab1
                                     Table "public.ab1"
@@ -131,6 +165,13 @@ ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
 Statistics objects:
     "public"."ab1_a_b_stats" (ndistinct, dependencies, mcv) ON a, b FROM ab1
 
+\dX+ ab1_a_b_stats
+                                        List of extended statistics
+ Schema |     Name      |  Definition   | N_distinct | Dependencies |   Mcv   | N_size  | D_size  | M_size  
+--------+---------------+---------------+------------+--------------+---------+---------+---------+---------
+ public | ab1_a_b_stats | a, b FROM ab1 | defined    | defined      | defined | 0 bytes | 0 bytes | 0 bytes
+(1 row)
+
 -- partial analyze doesn't build stats either
 ANALYZE ab1 (a);
 WARNING:  statistics object "public.ab1_a_b_stats" could not be computed for relation "public.ab1"
@@ -146,6 +187,13 @@ CREATE TABLE ab1c () INHERITS (ab1);
 INSERT INTO ab1 VALUES (1,1);
 CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
 ANALYZE ab1;
+\dX+ ab1_a_b_stats
+                                         List of extended statistics
+ Schema |     Name      |  Definition   | N_distinct | Dependencies |  Mcv  |  N_size  |  D_size  |  M_size   
+--------+---------------+---------------+------------+--------------+-------+----------+----------+-----------
+ public | ab1_a_b_stats | a, b FROM ab1 | built      | built        | built | 11 bytes | 40 bytes | 186 bytes
+(1 row)
+
 DROP TABLE ab1 CASCADE;
 NOTICE:  drop cascades to table ab1c
 -- Verify supported object types for extended statistics
@@ -254,6 +302,13 @@ SELECT s.stxkind, d.stxdndistinct
  {d,f,m} | {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
 (1 row)
 
+\dX+ s10
+                                          List of extended statistics
+ Schema | Name |       Definition       | N_distinct | Dependencies |  Mcv  |  N_size  |  D_size   |  M_size   
+--------+------+------------------------+------------+--------------+-------+----------+-----------+-----------
+ public | s10  | a, b, c FROM ndistinct | built      | built        | built | 51 bytes | 189 bytes | 988 bytes
+(1 row)
+
 -- minor improvement, make sure the ctid does not break the matching
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b');
  estimated | actual 
@@ -310,6 +365,13 @@ SELECT s.stxkind, d.stxdndistinct
  {d,f,m} | {"3, 4": 2550, "3, 6": 800, "4, 6": 1632, "3, 4, 6": 5000}
 (1 row)
 
+\dX+ s10
+                                          List of extended statistics
+ Schema | Name |       Definition       | N_distinct | Dependencies |  Mcv  |  N_size  |  D_size  |   M_size   
+--------+------+------------------------+------------+--------------+-------+----------+----------+------------
+ public | s10  | a, b, c FROM ndistinct | built      | built        | built | 58 bytes | 23 bytes | 5718 bytes
+(1 row)
+
 -- correct estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
  estimated | actual 
@@ -350,6 +412,12 @@ SELECT s.stxkind, d.stxdndistinct
 ---------+---------------
 (0 rows)
 
+\dX+
+                               List of extended statistics
+ Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size 
+--------+------+------------+------------+--------------+-----+--------+--------+--------
+(0 rows)
+
 -- dropping the statistics results in under-estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
  estimated | actual 
@@ -413,6 +481,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 -- create statistics
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
+                                                 List of extended statistics
+ Schema |      Name      |              Definition              | N_distinct | Dependencies | Mcv | N_size | D_size  | M_size 
+--------+----------------+--------------------------------------+------------+--------------+-----+--------+---------+--------
+ public | func_deps_stat | a, b, c FROM functional_dependencies |            | defined      |     |        | 0 bytes | 
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
  estimated | actual 
 -----------+--------
@@ -431,6 +506,12 @@ DROP STATISTICS func_deps_stat;
 INSERT INTO functional_dependencies (a, b, c, filler1)
      SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i);
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
+                               List of extended statistics
+ Schema | Name | Definition | N_distinct | Dependencies | Mcv | N_size | D_size | M_size 
+--------+------+------------+------------+--------------+-----+--------+--------+--------
+(0 rows)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
  estimated | actual 
 -----------+--------
@@ -590,6 +671,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 -- create statistics
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
+                                                  List of extended statistics
+ Schema |      Name      |              Definition              | N_distinct | Dependencies | Mcv | N_size |  D_size   | M_size 
+--------+----------------+--------------------------------------+------------+--------------+-----+--------+-----------+--------
+ public | func_deps_stat | a, b, c FROM functional_dependencies |            | built        |     |        | 106 bytes | 
+(1 row)
+
 -- print the detected dependencies
 SELECT dependencies FROM pg_stats_ext WHERE statistics_name = 'func_deps_stat';
                                                 dependencies                                                
@@ -821,6 +909,14 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi
 CREATE STATISTICS functional_dependencies_multi_1 (dependencies) ON a, b FROM functional_dependencies_multi;
 CREATE STATISTICS functional_dependencies_multi_2 (dependencies) ON c, d FROM functional_dependencies_multi;
 ANALYZE functional_dependencies_multi;
+\dX+ functional_dependencies_multi_*
+                                                            List of extended statistics
+ Schema |              Name               |               Definition                | N_distinct | Dependencies | Mcv | N_size |  D_size  | M_size 
+--------+---------------------------------+-----------------------------------------+------------+--------------+-----+--------+----------+--------
+ public | functional_dependencies_multi_1 | a, b FROM functional_dependencies_multi |            | built        |     |        | 40 bytes | 
+ public | functional_dependencies_multi_2 | c, d FROM functional_dependencies_multi |            | built        |     |        | 40 bytes | 
+(2 rows)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0');
  estimated | actual 
 -----------+--------
@@ -882,6 +978,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b =
 -- create statistics
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |   M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+------------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 5890 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
  estimated | actual 
 -----------+--------
@@ -1041,6 +1144,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY
 -- create statistics
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |   M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+------------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 6754 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
  estimated | actual 
 -----------+--------
@@ -1191,6 +1301,13 @@ SELECT d.stxdmcv IS NOT NULL
  t
 (1 row)
 
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |   M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+------------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 6754 bytes
+(1 row)
+
 -- check change of column type resets the MCV statistics
 ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
@@ -1200,6 +1317,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b =
 (1 row)
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |   M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+------------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 6950 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
  estimated | actual 
 -----------+--------
@@ -1250,6 +1374,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (0, 1) AN
 -- create statistics
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |   M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+------------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 6916 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL');
  estimated | actual 
 -----------+--------
@@ -1294,6 +1425,13 @@ SELECT m.*
      0 | {1,2,3} | {f,f,f} |         1 |              1
 (1 row)
 
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |  M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+-----------
+ public | mcv_lists_stats | a, b, c FROM mcv_lists |            |              | built |        |        | 258 bytes
+(1 row)
+
 -- 2 distinct combinations with NULL values, all in the MCV list
 TRUNCATE mcv_lists;
 DROP STATISTICS mcv_lists_stats;
@@ -1338,6 +1476,13 @@ SELECT m.*
      1 | {NULL,NULL,NULL} | {t,t,t} |       0.5 |           0.25
 (2 rows)
 
+\dX+ mcv_lists_stats
+                                             List of extended statistics
+ Schema |      Name       |       Definition       | N_distinct | Dependencies |  Mcv  | N_size | D_size |  M_size   
+--------+-----------------+------------------------+------------+--------------+-------+--------+--------+-----------
+ public | mcv_lists_stats | a, b, d FROM mcv_lists |            |              | built |        |        | 294 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x''');
  estimated | actual 
 -----------+--------
@@ -1385,6 +1530,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''167
 CREATE STATISTICS mcv_lists_uuid_stats (mcv) ON a, b, c
   FROM mcv_lists_uuid;
 ANALYZE mcv_lists_uuid;
+\dX+ mcv_lists_uuid_stats
+                                                List of extended statistics
+ Schema |         Name         |         Definition          | N_distinct | Dependencies |  Mcv  | N_size | D_size | M_size 
+--------+----------------------+-----------------------------+------------+--------------+-------+--------+--------+--------
+ public | mcv_lists_uuid_stats | a, b, c FROM mcv_lists_uuid |            |              | built |        |        | 11 kB
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc''');
  estimated | actual 
 -----------+--------
@@ -1414,6 +1566,13 @@ INSERT INTO mcv_lists_arrays (a, b, c)
 CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c
   FROM mcv_lists_arrays;
 ANALYZE mcv_lists_arrays;
+\dX+ mcv_lists_arrays_stats
+                                                  List of extended statistics
+ Schema |          Name          |          Definition           | N_distinct | Dependencies |  Mcv  | N_size | D_size | M_size 
+--------+------------------------+-------------------------------+------------+--------------+-------+--------+--------+--------
+ public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays |            |              | built |        |        | 24 kB
+(1 row)
+
 -- mcv with bool
 CREATE TABLE mcv_lists_bool (
     a BOOL,
@@ -1453,6 +1612,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE NOT a AND
 CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c
   FROM mcv_lists_bool;
 ANALYZE mcv_lists_bool;
+\dX+ mcv_lists_bool_stats
+                                                  List of extended statistics
+ Schema |         Name         |         Definition          | N_distinct | Dependencies |  Mcv  | N_size | D_size |  M_size   
+--------+----------------------+-----------------------------+------------+--------------+-------+--------+--------+-----------
+ public | mcv_lists_bool_stats | a, b, c FROM mcv_lists_bool |            |              | built |        |        | 386 bytes
+(1 row)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c');
  estimated | actual 
 -----------+--------
@@ -1516,6 +1682,14 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AN
 CREATE STATISTICS mcv_lists_multi_1 (mcv) ON a, b FROM mcv_lists_multi;
 CREATE STATISTICS mcv_lists_multi_2 (mcv) ON c, d FROM mcv_lists_multi;
 ANALYZE mcv_lists_multi;
+\dX+ mcv_lists_multi_*
+                                               List of extended statistics
+ Schema |       Name        |        Definition         | N_distinct | Dependencies |  Mcv  | N_size | D_size |  M_size   
+--------+-------------------+---------------------------+------------+--------------+-------+--------+--------+-----------
+ public | mcv_lists_multi_1 | a, b FROM mcv_lists_multi |            |              | built |        |        | 426 bytes
+ public | mcv_lists_multi_2 | c, d FROM mcv_lists_multi |            |              | built |        |        | 546 bytes
+(2 rows)
+
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0');
  estimated | actual 
 -----------+--------
@@ -1550,6 +1724,13 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+\dX+ tststats.priv_test_stats
+                                                   List of extended statistics
+  Schema  |      Name       |            Definition            | N_distinct | Dependencies |  Mcv  | N_size | D_size |  M_size   
+----------+-----------------+----------------------------------+------------+--------------+-------+--------+--------+-----------
+ tststats | priv_test_stats | a, b FROM tststats.priv_test_tbl |            |              | built |        |        | 686 bytes
+(1 row)
+
 -- Check printing info about extended statistics by \dX
 create table stts_t1 (a int, b int);
 create statistics stts_1 (ndistinct) on a, b from stts_t1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 2b90471a4b..40da1dcbb5 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -49,6 +49,7 @@ CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON a, b FROM ab1;
 
 -- Let's also verify the pg_get_statisticsobjdef output looks sane.
 SELECT pg_get_statisticsobjdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats';
+\dX
 
 DROP STATISTICS regress_schema_2.ab1_a_b_stats;
 
@@ -60,9 +61,10 @@ ALTER TABLE ab1 DROP COLUMN a;
 \d ab1
 -- Ensure statistics are dropped when table is
 SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
+\dX
 DROP TABLE ab1;
 SELECT stxname FROM pg_statistic_ext WHERE stxname LIKE 'ab1%';
-
+\dX
 -- Ensure things work sanely with SET STATISTICS 0
 CREATE TABLE ab1 (a INTEGER, b INTEGER);
 ALTER TABLE ab1 ALTER a SET STATISTICS 0;
@@ -73,13 +75,16 @@ ALTER TABLE ab1 ALTER a SET STATISTICS -1;
 -- setting statistics target 0 skips the statistics, without printing any message, so check catalog
 ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
 \d ab1
+\dX+ ab1_a_b_stats
 ANALYZE ab1;
 SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxname = 'ab1_a_b_stats'
    AND d.stxoid = s.oid;
+\dX+ ab1_a_b_stats
 ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
 \d+ ab1
+\dX+ ab1_a_b_stats
 -- partial analyze doesn't build stats either
 ANALYZE ab1 (a);
 ANALYZE ab1;
@@ -93,6 +98,7 @@ CREATE TABLE ab1c () INHERITS (ab1);
 INSERT INTO ab1 VALUES (1,1);
 CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
 ANALYZE ab1;
+\dX+ ab1_a_b_stats
 DROP TABLE ab1 CASCADE;
 
 -- Verify supported object types for extended statistics
@@ -171,6 +177,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+ s10
 
 -- minor improvement, make sure the ctid does not break the matching
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b');
@@ -202,6 +209,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+ s10
 
 -- correct estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -220,6 +228,7 @@ SELECT s.stxkind, d.stxdndistinct
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
+\dX+
 
 -- dropping the statistics results in under-estimates
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -261,6 +270,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
 
@@ -274,6 +284,7 @@ INSERT INTO functional_dependencies (a, b, c, filler1)
      SELECT mod(i,100), mod(i,50), mod(i,25), i FROM generate_series(1,5000) s(i);
 
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE a = 1 AND b = ''1''');
 
@@ -335,6 +346,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
+\dX+ func_deps_stat
 
 -- print the detected dependencies
 SELECT dependencies FROM pg_stats_ext WHERE statistics_name = 'func_deps_stat';
@@ -437,6 +449,7 @@ CREATE STATISTICS functional_dependencies_multi_1 (dependencies) ON a, b FROM fu
 CREATE STATISTICS functional_dependencies_multi_2 (dependencies) ON c, d FROM functional_dependencies_multi;
 
 ANALYZE functional_dependencies_multi;
+\dX+ functional_dependencies_multi_*
 
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE a = 0 AND b = 0');
 SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies_multi WHERE 0 = a AND 0 = b');
@@ -472,6 +485,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b =
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -536,6 +550,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a < ALL (ARRAY
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -591,6 +606,7 @@ SELECT d.stxdmcv IS NOT NULL
   FROM pg_statistic_ext s, pg_statistic_ext_data d
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dX+ mcv_lists_stats
 
 -- check change of column type resets the MCV statistics
 ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
@@ -598,6 +614,7 @@ ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a = 1 AND b = ''1''');
 
@@ -629,6 +646,7 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IN (0, 1) AN
 CREATE STATISTICS mcv_lists_stats (mcv) ON a, b, c FROM mcv_lists;
 
 ANALYZE mcv_lists;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND b IS NULL');
 
@@ -650,6 +668,7 @@ SELECT m.*
        pg_mcv_list_items(d.stxdmcv) m
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dX+ mcv_lists_stats
 
 -- 2 distinct combinations with NULL values, all in the MCV list
 TRUNCATE mcv_lists;
@@ -682,6 +701,7 @@ SELECT m.*
        pg_mcv_list_items(d.stxdmcv) m
  WHERE s.stxname = 'mcv_lists_stats'
    AND d.stxoid = s.oid;
+\dX+ mcv_lists_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE b = ''x'' OR d = ''x''');
 
@@ -714,6 +734,7 @@ CREATE STATISTICS mcv_lists_uuid_stats (mcv) ON a, b, c
   FROM mcv_lists_uuid;
 
 ANALYZE mcv_lists_uuid;
+\dX+ mcv_lists_uuid_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_uuid WHERE a = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc'' AND b = ''1679091c-5a88-0faf-6fb5-e6087eb1b2dc''');
 
@@ -740,6 +761,7 @@ CREATE STATISTICS mcv_lists_arrays_stats (mcv) ON a, b, c
   FROM mcv_lists_arrays;
 
 ANALYZE mcv_lists_arrays;
+\dX+ mcv_lists_arrays_stats
 
 -- mcv with bool
 CREATE TABLE mcv_lists_bool (
@@ -768,6 +790,7 @@ CREATE STATISTICS mcv_lists_bool_stats (mcv) ON a, b, c
   FROM mcv_lists_bool;
 
 ANALYZE mcv_lists_bool;
+\dX+ mcv_lists_bool_stats
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_bool WHERE a AND b AND c');
 
@@ -806,6 +829,7 @@ CREATE STATISTICS mcv_lists_multi_1 (mcv) ON a, b FROM mcv_lists_multi;
 CREATE STATISTICS mcv_lists_multi_2 (mcv) ON c, d FROM mcv_lists_multi;
 
 ANALYZE mcv_lists_multi;
+\dX+ mcv_lists_multi_*
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE a = 0 AND b = 0');
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_multi WHERE c = 0 AND d = 0');
@@ -832,6 +856,7 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 
 ANALYZE tststats.priv_test_tbl;
+\dX+ tststats.priv_test_stats
 
 -- Check printing info about extended statistics by \dX
 create table stts_t1 (a int, b int);
-- 
2.26.2

#43Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#42)
2 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

Thanks for your comments and also revising patches.

On 2020/11/16 3:22, Tomas Vondra wrote:

It's better to always post the whole patch series, so that cfbot can
test it properly. Sending just 0003 separately kind breaks that.

I now understand how "cfbot" works so that I'll take care of that
when I send patches. Thanks.

Also, 0003 seems to only tweak the .sql file, not the expected output,
and there actually seems to be two places that mistakenly use \dx (so
listing extensions) instead of \dX. I've fixed both issues in the
attached patches.

Oops, sorry about that.

However, I think the 0002 tests are better/sufficient - I prefer to keep
it compact, not interleaving with the tests testing various other stuff.
So I don't intend to commit 0003, unless there's something that I don't
see for some reason.

I Agreed. 0002 is easy to modify test cases and check results than 0003.
Therefore, I'll go with 0002.

The one remaining thing I'm not sure about is naming of the columns with
size of statistics - N_size, D_size and M_size does not seem very clear.
Any clearer naming will however make the tables wider, though :-/

Yeah, I think so too, but I couldn't get an idea of a suitable name for
the columns when I created the patch.
I don't prefer a long name but I'll replace the name with it to be clearer.
For example, s/N_size/Ndistinct_size/.

Please find attached patcheds:
- 0001: Replace column names
- 0002: Recreate regression test based on 0001

Regards,
Tatsuro Yamada

Attachments:

0002-v2-Add-regression-test-of-dX-command.patchtext/plain; charset=UTF-8; name=0002-v2-Add-regression-test-of-dX-command.patchDownload
From 85fe05c3020cd595ae8d5c2cc6f695b39f4a6e03 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 17 Nov 2020 13:30:57 +0900
Subject: [PATCH 2/2] Recreate regression test

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 4c3edd213f..27ca54a8f3 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1550,6 +1550,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 9781e590a3..2b90471a4b 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -833,6 +833,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

0001-v9-Add-dX-command-on-psql.patchtext/plain; charset=UTF-8; name=0001-v9-Add-dX-command-on-psql.patchDownload
From b093c5cc9b6376809123ca90bec3f330109935b8 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 17 Nov 2020 13:28:50 +0900
Subject: [PATCH 1/2] Replace column names with long names.

For example, s/N_size/Ndistinct_size/.
---
 doc/src/sgml/ref/psql-ref.sgml |  14 ++++++
 src/bin/psql/command.c         |   3 ++
 src/bin/psql/describe.c        | 100 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 ++
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c7a83d5dfc..c6f1653cb7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -930,6 +930,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 07d640021c..3eb3b94b14 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4397,6 +4397,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("Ndistinct_size"),
+						  gettext_noop("Dependencies_size"),
+						  gettext_noop("MCV_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5238a960f7..30a8ada7d2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3851,6 +3851,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

#44Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#43)
2 attachment(s)
Re: list of extended statistics on psql

Hi Tomas and hackers,

I don't prefer a long name but I'll replace the name with it to be clearer.
For example, s/N_size/Ndistinct_size/.

Please find attached patcheds:
  - 0001: Replace column names
  - 0002: Recreate regression test based on 0001

I rebased the patch set on the master (7e5e1bba03), and the regression
test is good. Therefore, I changed the status of the patch: "needs review".

I know that you proposed the new extended statistics[1]/messages/by-id/ad7891d2-e90c-b446-9fe2-7419143847d7@enterprisedb.com, and it probably
conflicts with the patch. I hope my patch will get commit before your
patch committed to avoid the time of recreating. :-)

[1]: /messages/by-id/ad7891d2-e90c-b446-9fe2-7419143847d7@enterprisedb.com

Thanks,
Tatsuro Yamada

Attachments:

0001-Add-dX-command-on-psql-rebased-on-7e5e1bba03.patchtext/plain; charset=UTF-8; name=0001-Add-dX-command-on-psql-rebased-on-7e5e1bba03.patchDownload
From 91c09db61c2891cf83b3151f51348dfd02e09744 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Mon, 30 Nov 2020 11:09:00 +0900
Subject: [PATCH 1/2] Add \dX command on psql (rebased on 7e5e1bba03)

---
 doc/src/sgml/ref/psql-ref.sgml |  14 ++++++
 src/bin/psql/command.c         |   3 ++
 src/bin/psql/describe.c        | 100 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 ++
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 38b588882d..46a6d0df76 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 14150d05a9..7dac038f1b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4401,6 +4401,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("Ndistinct_size"),
+						  gettext_noop("Dependencies_size"),
+						  gettext_noop("MCV_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f0e3ec957c..89b13c3f0c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index af829282e6..ea249bf96d 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8afc780acc..0112363b10 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3898,6 +3898,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

0002-Add-regression-test-for-dX-rebased-on-7e5e1bba03.patchtext/plain; charset=UTF-8; name=0002-Add-regression-test-for-dX-rebased-on-7e5e1bba03.patchDownload
From da9c7378c7d049758d8d74798e7076f00e4f07c6 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Mon, 30 Nov 2020 11:10:45 +0900
Subject: [PATCH 2/2] Add regression test for \dX (rebased on 7e5e1bba03)

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 4c3edd213f..27ca54a8f3 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1550,6 +1550,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 9781e590a3..2b90471a4b 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -833,6 +833,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

#45Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#44)
2 attachment(s)
Re: list of extended statistics on psql

Hi,

I rebased the patch set on the master (7e5e1bba03), and the regression
test is good. Therefore, I changed the status of the patch: "needs review".

Happy New Year!

I rebased my patches on HEAD.
Please find attached files. :-D

Thanks,
Tatsuro Yamada

Attachments:

0002-Add-regression-test-for-dX-on-psql.patchtext/plain; charset=UTF-8; name=0002-Add-regression-test-for-dX-on-psql.patchDownload
From 9f36a9df0c2803f5554b951e37ba5969c2744e3b Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 5 Jan 2021 12:34:16 +0900
Subject: [PATCH 2/2] Add regression test for \dX on psql

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 7bfeaf85f0..8c8a0afcf6 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1725,6 +1725,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 7912e733ae..db6e3e1ba3 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -912,6 +912,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

0001-Add-dX-command-on-psql.patchtext/plain; charset=UTF-8; name=0001-Add-dX-command-on-psql.patchDownload
From fe32b38c7b0d2ce2c4238eba91a2a8ba9229e994 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 5 Jan 2021 12:32:45 +0900
Subject: [PATCH 1/2] Add \dX command on psql

---
 doc/src/sgml/ref/psql-ref.sgml |  14 ++++++
 src/bin/psql/command.c         |   3 ++
 src/bin/psql/describe.c        | 100 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 ++
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c5ebc1c3f4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 52c6de51b6..f90070a064 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4401,6 +4401,106 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\", \n"
+					  "CASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+					  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"),
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"),
+					  gettext_noop("MCV"));
+
+	if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("Ndistinct_size"),
+						  gettext_noop("Dependencies_size"),
+						  gettext_noop("MCV_size"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n"
+						 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+						 "ON es.oid = esd.stxoid \n"
+						 "INNER JOIN pg_catalog.pg_class c \n"
+						 "ON es.stxrelid = c.oid \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..867e57d851 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..e42bc8c54e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..611f1efb15 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3910,6 +3910,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

#46Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#45)
Re: list of extended statistics on psql

On 1/5/21 5:26 AM, Tatsuro Yamada wrote:

Hi,

I rebased the patch set on the master (7e5e1bba03), and the regression
test is good. Therefore, I changed the status of the patch: "needs
review".

Happy New Year!

I rebased my patches on HEAD.
Please find attached files. :-D

Thanks, and Happy new year to you too.

I almost pushed this, but I have one more question. listExtendedStats
first checks the server version, and errors out for pre-10 servers.
Shouldn't the logic building query check the server version too, to
decide whether to check the MCV stats? Otherwise it won't work on 10 and
11, which does not support the "mcv" stats.

I don't recall what exactly is our policy regarding new psql with older
server versions, but it seems strange to check for 10.0 and then fail
anyway for "supported" versions.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#47Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#46)
Re: list of extended statistics on psql

Hi Tomas,

Thanks, and Happy new year to you too.

I almost pushed this, but I have one more question. listExtendedStats first checks the server version, and errors out for pre-10 servers. Shouldn't the logic building query check the server version too, to decide whether to check the MCV stats? Otherwise it won't work on 10 and 11, which does not support the "mcv" stats.

I don't recall what exactly is our policy regarding new psql with older server versions, but it seems strange to check for 10.0 and then fail anyway for "supported" versions.

Thanks for your comments.

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

I wonder the column names added by \dX+ is fine? For example,
"Ndistinct_size" and "Dependencies_size". It looks like long names,
but acceptable?

Regards,
Tatsuro Yamada

#48Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#47)
Re: list of extended statistics on psql

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

Hi Tomas,

Thanks, and Happy new year to you too.

I almost pushed this, but I have one more question. listExtendedStats
first checks the server version, and errors out for pre-10 servers.
Shouldn't the logic building query check the server version too, to
decide whether to check the MCV stats? Otherwise it won't work on 10
and 11, which does not support the "mcv" stats.

I don't recall what exactly is our policy regarding new psql with
older server versions, but it seems strange to check for 10.0 and
then fail anyway for "supported" versions.

Thanks for your comments.

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

+1

BTW perhaps a quick look at the other \d commands would show if there
are precedents. I didn't have time for that.

I wonder the column names added by \dX+ is fine? For example,
"Ndistinct_size" and "Dependencies_size". It looks like long names,
but acceptable?

Seems acceptable - I don't have a better idea.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#49Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#48)
Re: list of extended statistics on psql

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

I think we would not backpatch any of this, though.

--
�lvaro Herrera

#50Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Alvaro Herrera (#49)
Re: list of extended statistics on psql

On 1/7/21 3:47 PM, Alvaro Herrera wrote:

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

Yeah, makes sense. That means we need add the check for 12 / MCV.

I think we would not backpatch any of this, though.

I wasn't really planning to backpatch any of this, of course.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#51Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#50)
2 attachment(s)
Re: list of extended statistics on psql

Hi,

On 2021/01/08 0:56, Tomas Vondra wrote:

On 1/7/21 3:47 PM, Alvaro Herrera wrote:

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

Yeah, makes sense. That means we need add the check for 12 / MCV.

Ah, I got it.
I fixed the patch to work with older servers to add the checking versions. And I tested \dX command on older servers (PG10 - 13).
These results look fine.

0001:
Added the check code to handle pre-PG12. It has not MCV and
pg_statistic_ext_data.
0002:
This patch is the same as the previous patch (not changed).

Please find the attached files.

I wonder the column names added by \dX+ is fine? For example,
"Ndistinct_size" and "Dependencies_size". It looks like long names,
but acceptable?

Seems acceptable - I don't have a better idea.

I see, thanks!

Thanks,
Tatsuro Yamada

Attachments:

0002-Add-regression-test-for-dX-on-psql.patchtext/plain; charset=UTF-8; name=0002-Add-regression-test-for-dX-on-psql.patchDownload
From 9f36a9df0c2803f5554b951e37ba5969c2744e3b Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Tue, 5 Jan 2021 12:34:16 +0900
Subject: [PATCH 2/2] Add regression test for \dX on psql

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 7bfeaf85f0..8c8a0afcf6 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1725,6 +1725,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 7912e733ae..db6e3e1ba3 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -912,6 +912,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

0001-Add-dX-command-on-psql-with-version-check.patchtext/plain; charset=UTF-8; name=0001-Add-dX-command-on-psql-with-version-check.patchDownload
From c8be51a52a381a6e9c7be62022f5fc48b5915bd0 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Thu, 7 Jan 2021 14:28:20 +0900
Subject: [PATCH] Add \dX command on psql

This patch includes the version check in the logic building query.
---
 doc/src/sgml/ref/psql-ref.sgml |  14 ++++
 src/bin/psql/command.c         |   3 +
 src/bin/psql/describe.c        | 141 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 +
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 165 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..fd860776af 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the pattern
+        are listed.
+        If <literal>+</literal> is appended to the command name, each extended statistics
+        is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c5ebc1c3f4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 52c6de51b6..0ccd9ed286 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4401,6 +4401,147 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Briefly describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"));
+	}
+	else
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"),
+						  gettext_noop("MCV"));
+	}
+
+	if (verbose && pset.sversion < 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN es.stxdependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxdependencies)::bigint) \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct_size"),
+						  gettext_noop("Dependencies_size"));
+	}
+	else if (verbose)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdndistinct)::bigint) \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxddependencies)::bigint) \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+						  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdmcv)::bigint) \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN '0 bytes' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct_size"),
+						  gettext_noop("Dependencies_size"),
+						  gettext_noop("MCV_size"));
+	}
+
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+	else
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+							 "ON es.oid = esd.stxoid \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..867e57d851 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..e42bc8c54e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..611f1efb15 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3910,6 +3910,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

#52Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#51)
Re: list of extended statistics on psql

On 1/8/21 12:52 AM, Tatsuro Yamada wrote:

Hi,

On 2021/01/08 0:56, Tomas Vondra wrote:

On 1/7/21 3:47 PM, Alvaro Herrera wrote:

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if
there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

Yeah, makes sense. That means we need add the check for 12 / MCV.

Ah, I got it.
I fixed the patch to work with older servers to add the checking
versions. And I tested \dX command on older servers (PG10 - 13).
These results look fine.

0001:
     Added the check code to handle pre-PG12. It has not MCV and
      pg_statistic_ext_data.
0002:
     This patch is the same as the previous patch (not changed).

Please find the attached files.

OK, thanks. I'll take a look and probably push tomorrow. FWIW I plan to
squash the patches into a single commit.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#53Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tomas Vondra (#52)
2 attachment(s)
Re: list of extended statistics on psql

On 1/8/21 1:14 AM, Tomas Vondra wrote:

On 1/8/21 12:52 AM, Tatsuro Yamada wrote:

Hi,

On 2021/01/08 0:56, Tomas Vondra wrote:

On 1/7/21 3:47 PM, Alvaro Herrera wrote:

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if
there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

Yeah, makes sense. That means we need add the check for 12 / MCV.

Ah, I got it.
I fixed the patch to work with older servers to add the checking
versions. And I tested \dX command on older servers (PG10 - 13).
These results look fine.

0001:
      Added the check code to handle pre-PG12. It has not MCV and
       pg_statistic_ext_data.
0002:
      This patch is the same as the previous patch (not changed).

Please find the attached files.

OK, thanks. I'll take a look and probably push tomorrow. FWIW I plan to
squash the patches into a single commit.

Attached is a patch I plan to commit - 0001 is the last submitted
version with a couple minor tweaks, mostly in docs/comments, and small
rework of branching to be more like the other functions in describe.c.

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

0001-psql-dX-list-extended-statistics-objects-20210109.patchtext/x-patch; charset=UTF-8; name=0001-psql-dX-list-extended-statistics-objects-20210109.patchDownload
From 3d2f4ef2ecba9fd7987df665237add6fc4ec03c1 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Thu, 7 Jan 2021 14:28:20 +0900
Subject: [PATCH 1/2] psql \dX: list extended statistics objects

The new command lists extended statistics objects, possibly with their
sizes. All past releases with extended statistics are supported.

Author: Tatsuro Yamada
Reviewed-by: Julien Rouhaud, Alvaro Herrera, Tomas Vondra
Discussion: https://postgr.es/m/c027a541-5856-75a5-0868-341301e1624b%40nttcom.co.jp_1
---
 doc/src/sgml/ref/psql-ref.sgml          |  14 +++
 src/bin/psql/command.c                  |   3 +
 src/bin/psql/describe.c                 | 150 ++++++++++++++++++++++++
 src/bin/psql/describe.h                 |   3 +
 src/bin/psql/help.c                     |   1 +
 src/bin/psql/tab-complete.c             |   4 +-
 src/test/regress/expected/stats_ext.out |  94 +++++++++++++++
 src/test/regress/sql/stats_ext.sql      |  31 +++++
 8 files changed, 299 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..d01acc92b8 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the
+        pattern are listed.
+        If <literal>+</literal> is appended to the command name, each extended
+        statistics is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c5ebc1c3f4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index caf97563f4..46f54199fb 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4392,6 +4392,156 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	/*
+	 * Since 12 there are two catalogs - one for the definition, one for the
+	 * data built by ANALYZE. Older releases use a single catalog. Also, 12
+	 * adds the MCV statistics kind.
+	 */
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"));
+	}
+	else
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"),
+						  gettext_noop("MCV"));
+	}
+
+	/* In verbose mode, print sizes of the extended statistics objects. */
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN es.stxdependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxdependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"));
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxddependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdmcv)::bigint) \n"
+							  "     WHEN 'm' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"),
+							  gettext_noop("MCV_size"));
+		}
+	}
+
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+	else
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+							 "ON es.oid = esd.stxoid \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..867e57d851 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..e42bc8c54e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..611f1efb15 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3910,6 +3910,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 7bfeaf85f0..8c8a0afcf6 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1725,6 +1725,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 7912e733ae..db6e3e1ba3 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -912,6 +912,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.26.2

0002-fixup-rename-defined-to-requested-20210109.patchtext/x-patch; charset=UTF-8; name=0002-fixup-rename-defined-to-requested-20210109.patchDownload
From 1ff2e77fd2472700a4a89699d8afed8679e38c7e Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sat, 9 Jan 2021 00:56:43 +0100
Subject: [PATCH 2/2] fixup: rename defined to requested

---
 src/bin/psql/describe.c                 | 10 +--
 src/test/regress/expected/stats_ext.out | 90 ++++++++++++-------------
 2 files changed, 50 insertions(+), 50 deletions(-)

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 46f54199fb..83084f79d0 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4440,10 +4440,10 @@ listExtendedStats(const char *pattern, bool verbose)
 	{
 		appendPQExpBuffer(&buf,
 						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\"",
 						  gettext_noop("Ndistinct"),
 						  gettext_noop("Dependencies"));
@@ -4452,13 +4452,13 @@ listExtendedStats(const char *pattern, bool verbose)
 	{
 		appendPQExpBuffer(&buf,
 						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\"",
 						  gettext_noop("Ndistinct"),
 						  gettext_noop("Dependencies"),
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 8c8a0afcf6..b3ae4de185 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1741,9 +1741,9 @@ create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_
 insert into stts_t1 select i,i from generate_series(1,100) i;
 analyze stts_t1;
 \dX
-                                          List of extended statistics
-  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
-----------+------------------------+--------------------------------------+-----------+--------------+---------
+                                           List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    
+----------+------------------------+--------------------------------------+-----------+--------------+-----------
  public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
  public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
  public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
@@ -1751,70 +1751,70 @@ analyze stts_t1;
  public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
  public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
  public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
- public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
- public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
- stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
- stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested
  tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
 (12 rows)
 
 \dX stts_?
-                       List of extended statistics
- Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
---------+--------+-------------------+-----------+--------------+---------
+                        List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    
+--------+--------+-------------------+-----------+--------------+-----------
  public | stts_1 | a, b FROM stts_t1 | built     |              | 
  public | stts_2 | a, b FROM stts_t1 | built     | built        | 
  public | stts_3 | a, b FROM stts_t1 | built     | built        | built
- public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested
 (4 rows)
 
 \dX *stts_hoge
-                               List of extended statistics
- Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
---------+-----------+-------------------------------+-----------+--------------+---------
- public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+                                List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    
+--------+-----------+-------------------------------+-----------+--------------+-----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested
 (1 row)
 
 \dX+
-                                                                   List of extended statistics
-  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
-----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
- public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
- public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
- public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
- public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
- public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
- public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
- public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
- public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
- tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+                                                                    List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |           |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built     |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built     |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built     |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |           | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built     |                |                   | 686 bytes
 (12 rows)
 
 \dX+ stts_?
-                                                List of extended statistics
- Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
---------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
- public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
- public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
- public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
- public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+                                                 List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |           | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
 (4 rows)
 
 \dX+ *stts_hoge
-                                                       List of extended statistics
- Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
---------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
- public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+                                                        List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
 (1 row)
 
 \dX+ stts_s2.stts_yama
-                                                    List of extended statistics
- Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
----------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
- stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+                                                     List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | requested    | requested |                | 0 bytes           | 0 bytes
 (1 row)
 
 drop table stts_t1, stts_t2, stts_t3;
-- 
2.26.2

#54Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#53)
2 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/09 9:01, Tomas Vondra wrote:

On 1/8/21 1:14 AM, Tomas Vondra wrote:

On 1/8/21 12:52 AM, Tatsuro Yamada wrote:

On 2021/01/08 0:56, Tomas Vondra wrote:

On 1/7/21 3:47 PM, Alvaro Herrera wrote:

On 2021-Jan-07, Tomas Vondra wrote:

On 1/7/21 1:46 AM, Tatsuro Yamada wrote:

I overlooked the check for MCV in the logic building query
because I created the patch as a new feature on PG14.
I'm not sure whether we should do back patch or not. However, I'll
add the check on the next patch because it is useful if you decide to
do the back patch on PG10, 11, 12, and 13.

BTW perhaps a quick look at the other \d commands would show if
there are
precedents. I didn't have time for that.

Yes, we do promise that new psql works with older servers.

Yeah, makes sense. That means we need add the check for 12 / MCV.

Ah, I got it.
I fixed the patch to work with older servers to add the checking
versions. And I tested \dX command on older servers (PG10 - 13).
These results look fine.

0001:
      Added the check code to handle pre-PG12. It has not MCV and
       pg_statistic_ext_data.
0002:
      This patch is the same as the previous patch (not changed).

Please find the attached files.

OK, thanks. I'll take a look and probably push tomorrow. FWIW I plan to
squash the patches into a single commit.

Attached is a patch I plan to commit - 0001 is the last submitted
version with a couple minor tweaks, mostly in docs/comments, and small
rework of branching to be more like the other functions in describe.c.

Thanks for revising the patch.
I reviewed the 0001, and the branching and comments look good to me.
However, I added an alias name in processSQLNamePattern() on the patch:
s/"stxname"/"es.stxname"/

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

- "defined": it shows the extended stats defined only. We can't know
whether it needs to analyze or not. I agree this name was
ambiguous. Therefore we should replace it with a more suitable
name.
- "requested": it shows the extended stats needs something. Of course,
we know it needs to ANALYZE because we can create the patch.
However, I feel there is a little ambiguity for DBA.
To solve this, it would be better to write an explanation of
the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Thanks,
Tatsuro Yamada

Attachments:

0001-psql-dX-list-extended-statistics-objects-20210112.patchtext/plain; charset=UTF-8; name=0001-psql-dX-list-extended-statistics-objects-20210112.patchDownload
>From 3d2f4ef2ecba9fd7987df665237add6fc4ec03c1 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Thu, 7 Jan 2021 14:28:20 +0900
Subject: [PATCH 1/2] psql \dX: list extended statistics objects

The new command lists extended statistics objects, possibly with their
sizes. All past releases with extended statistics are supported.

Author: Tatsuro Yamada
Reviewed-by: Julien Rouhaud, Alvaro Herrera, Tomas Vondra
Discussion: https://postgr.es/m/c027a541-5856-75a5-0868-341301e1624b%40nttcom.co.jp_1
---
 doc/src/sgml/ref/psql-ref.sgml          |  14 +++
 src/bin/psql/command.c                  |   3 +
 src/bin/psql/describe.c                 | 150 ++++++++++++++++++++++++
 src/bin/psql/describe.h                 |   3 +
 src/bin/psql/help.c                     |   1 +
 src/bin/psql/tab-complete.c             |   4 +-
 src/test/regress/expected/stats_ext.out |  94 +++++++++++++++
 src/test/regress/sql/stats_ext.sql      |  31 +++++
 8 files changed, 299 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..d01acc92b8 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,20 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the
+        pattern are listed.
+        If <literal>+</literal> is appended to the command name, each extended
+        statistics is listed with its size.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c5ebc1c3f4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index caf97563f4..46f54199fb 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4392,6 +4392,156 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	/*
+	 * Since 12 there are two catalogs - one for the definition, one for the
+	 * data built by ANALYZE. Older releases use a single catalog. Also, 12
+	 * adds the MCV statistics kind.
+	 */
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"));
+	}
+	else
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"),
+						  gettext_noop("MCV"));
+	}
+
+	/* In verbose mode, print sizes of the extended statistics objects. */
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN es.stxdependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxdependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"));
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxddependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdmcv)::bigint) \n"
+							  "     WHEN 'm' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"),
+							  gettext_noop("MCV_size"));
+		}
+	}
+
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+	else
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+							 "ON es.oid = esd.stxoid \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "es.stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..867e57d851 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..e42bc8c54e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..611f1efb15 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3910,6 +3910,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 7bfeaf85f0..8c8a0afcf6 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1725,6 +1725,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                                                   List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                       List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                    List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 7912e733ae..db6e3e1ba3 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -912,6 +912,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.26.2

0002-fixup-rename-defined-to-requested-20210109.patchtext/plain; charset=UTF-8; name=0002-fixup-rename-defined-to-requested-20210109.patchDownload
>From 1ff2e77fd2472700a4a89699d8afed8679e38c7e Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sat, 9 Jan 2021 00:56:43 +0100
Subject: [PATCH 2/2] fixup: rename defined to requested

---
 src/bin/psql/describe.c                 | 10 +--
 src/test/regress/expected/stats_ext.out | 90 ++++++++++++-------------
 2 files changed, 50 insertions(+), 50 deletions(-)

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 46f54199fb..83084f79d0 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4440,10 +4440,10 @@ listExtendedStats(const char *pattern, bool verbose)
 	{
 		appendPQExpBuffer(&buf,
 						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\"",
 						  gettext_noop("Ndistinct"),
 						  gettext_noop("Dependencies"));
@@ -4452,13 +4452,13 @@ listExtendedStats(const char *pattern, bool verbose)
 	{
 		appendPQExpBuffer(&buf,
 						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\", \n"
 						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
-						  "     WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'requested' \n"
 						  "END AS \"%s\"",
 						  gettext_noop("Ndistinct"),
 						  gettext_noop("Dependencies"),
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 8c8a0afcf6..b3ae4de185 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1741,9 +1741,9 @@ create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_
 insert into stts_t1 select i,i from generate_series(1,100) i;
 analyze stts_t1;
 \dX
-                                          List of extended statistics
-  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
-----------+------------------------+--------------------------------------+-----------+--------------+---------
+                                           List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    
+----------+------------------------+--------------------------------------+-----------+--------------+-----------
  public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
  public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
  public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
@@ -1751,70 +1751,70 @@ analyze stts_t1;
  public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
  public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
  public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
- public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
- public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
- stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
- stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested
  tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
 (12 rows)
 
 \dX stts_?
-                       List of extended statistics
- Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
---------+--------+-------------------+-----------+--------------+---------
+                        List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    
+--------+--------+-------------------+-----------+--------------+-----------
  public | stts_1 | a, b FROM stts_t1 | built     |              | 
  public | stts_2 | a, b FROM stts_t1 | built     | built        | 
  public | stts_3 | a, b FROM stts_t1 | built     | built        | built
- public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested
 (4 rows)
 
 \dX *stts_hoge
-                               List of extended statistics
- Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
---------+-----------+-------------------------------+-----------+--------------+---------
- public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+                                List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    
+--------+-----------+-------------------------------+-----------+--------------+-----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested
 (1 row)
 
 \dX+
-                                                                   List of extended statistics
-  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
-----------+------------------------+--------------------------------------+-----------+--------------+---------+----------------+-------------------+------------
- public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |         |                | 106 bytes         | 
- public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built   |                |                   | 24 kB
- public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built   |                |                   | 386 bytes
- public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built   |                |                   | 294 bytes
- public   | stts_1                 | a, b FROM stts_t1                    | built     |              |         | 13 bytes       |                   | 
- public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |         | 13 bytes       | 40 bytes          | 
- public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
- public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
- stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined |                | 0 bytes           | 0 bytes
- tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built   |                |                   | 686 bytes
+                                                                    List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |           |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built     |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built     |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built     |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |           | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built     |                |                   | 686 bytes
 (12 rows)
 
 \dX+ stts_?
-                                                List of extended statistics
- Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size |  MCV_size  
---------+--------+-------------------+-----------+--------------+---------+----------------+-------------------+------------
- public | stts_1 | a, b FROM stts_t1 | built     |              |         | 13 bytes       |                   | 
- public | stts_2 | a, b FROM stts_t1 | built     | built        |         | 13 bytes       | 40 bytes          | 
- public | stts_3 | a, b FROM stts_t1 | built     | built        | built   | 13 bytes       | 40 bytes          | 6126 bytes
- public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+                                                 List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |           | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
 (4 rows)
 
 \dX+ *stts_hoge
-                                                       List of extended statistics
- Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
---------+-----------+-------------------------------+-----------+--------------+---------+----------------+-------------------+----------
- public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined | 0 bytes        | 0 bytes           | 0 bytes
+                                                        List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
 (1 row)
 
 \dX+ stts_s2.stts_yama
-                                                    List of extended statistics
- Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   | Ndistinct_size | Dependencies_size | MCV_size 
----------+-----------+-------------------------+-----------+--------------+---------+----------------+-------------------+----------
- stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined |                | 0 bytes           | 0 bytes
+                                                     List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | requested    | requested |                | 0 bytes           | 0 bytes
 (1 row)
 
 drop table stts_t1, stts_t2, stts_t3;
-- 
2.26.2

#55Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#54)
Re: list of extended statistics on psql

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

 - "defined": it shows the extended stats defined only. We can't know
              whether it needs to analyze or not. I agree this name was
               ambiguous. Therefore we should replace it with a more
suitable
              name.
 - "requested": it shows the extended stats needs something. Of course,
              we know it needs to ANALYZE because we can create the patch.
              However, I feel there is a little ambiguity for DBA.
              To solve this, it would be better to write an explanation of
              the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some
statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means
ANALYZE
 was finished, and the planner can use it. NULL means that it doesn't
exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#56Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#55)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

  - "defined": it shows the extended stats defined only. We can't know
               whether it needs to analyze or not. I agree this name was
                ambiguous. Therefore we should replace it with a more suitable
               name.
  - "requested": it shows the extended stats needs something. Of course,
               we know it needs to ANALYZE because we can create the patch.
               However, I feel there is a little ambiguity for DBA.
               To solve this, it would be better to write an explanation of
               the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
  was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

Thanks,
Tatsuro Yamada

#57Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#56)
3 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/13 7:48, Tatsuro Yamada wrote:

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

  - "defined": it shows the extended stats defined only. We can't know
               whether it needs to analyze or not. I agree this name was
                ambiguous. Therefore we should replace it with a more suitable
               name.
  - "requested": it shows the extended stats needs something. Of course,
               we know it needs to ANALYZE because we can create the patch.
               However, I feel there is a little ambiguity for DBA.
               To solve this, it would be better to write an explanation of
               the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
  was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

I updated the patch to add the explanation of the extended stats' statuses.
Please feel free to modify the patch to improve it more clearly.

The attached files are:
0001: Add psql \dx and the fixed document
0002: Regression test for psql \dX
app-psql.html: Created by "make html" command (You can check the
explanation of the statuses easily, probably)

Thanks,
Tatsuro Yamada

Attachments:

0001-psql-dX-list-extended-statistics-objects-20210113.patchtext/plain; charset=UTF-8; name=0001-psql-dX-list-extended-statistics-objects-20210113.patchDownload
From 4dfc49faf0db33dfa5e4fa9bf38d31e4170ac459 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Wed, 13 Jan 2021 09:47:48 +0900
Subject: [PATCH 1/2] psql \dX: list extended statistics objects

The new command lists extended statistics objects, possibly with their
sizes. All past releases with extended statistics are supported.

Author: Tatsuro Yamada
Reviewed-by: Julien Rouhaud, Alvaro Herrera, Tomas Vondra
Discussion: https://postgr.es/m/c027a541-5856-75a5-0868-341301e1624b%40nttcom.co.jp_1
---
 doc/src/sgml/ref/psql-ref.sgml |  22 ++++++
 src/bin/psql/command.c         |   3 +
 src/bin/psql/describe.c        | 150 +++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |   3 +
 src/bin/psql/help.c            |   1 +
 src/bin/psql/tab-complete.c    |   4 +-
 6 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..aaf55df921 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,28 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the
+        pattern are listed.
+        If <literal>+</literal> is appended to the command name, each extended
+        statistics is listed with its size.
+        </para>
+
+        <para>
+        The column of the kind of extended stats (e.g. Ndistinct) shows some statuses.
+        "requested" means that it needs to collect statistics by <link
+        linkend="sql-analyze"><command>ANALYZE</command></link>. 
+        "built" means <link linkend="sql-analyze"><command>ANALYZE</command></link> was 
+        finished, and the planner can use it. NULL means that it doesn't exists. 
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c5ebc1c3f4 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern, show_verbose);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index caf97563f4..c2051ee820 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4392,6 +4392,156 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern, bool verbose)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	/*
+	 * Since 12 there are two catalogs - one for the definition, one for the
+	 * data built by ANALYZE. Older releases use a single catalog. Also, 12
+	 * adds the MCV statistics kind.
+	 */
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN es.stxdependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"));
+	}
+	else
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'd' = any(es.stxkind) THEN 'requested' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxddependencies IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'f' = any(es.stxkind) THEN 'requested' \n"
+						  "END AS \"%s\", \n"
+						  "CASE WHEN esd.stxdmcv IS NOT NULL THEN 'built' \n"
+						  "     WHEN 'm' = any(es.stxkind) THEN 'requested' \n"
+						  "END AS \"%s\"",
+						  gettext_noop("Ndistinct"),
+						  gettext_noop("Dependencies"),
+						  gettext_noop("MCV"));
+	}
+
+	/* In verbose mode, print sizes of the extended statistics objects. */
+	if (verbose)
+	{
+		if (pset.sversion < 120000)
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN es.stxndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN es.stxdependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(es.stxdependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"));
+		}
+		else
+		{
+			appendPQExpBuffer(&buf,
+							  ",\nCASE WHEN esd.stxdndistinct IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdndistinct)::bigint) \n"
+							  "     WHEN 'd' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxddependencies IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxddependencies)::bigint) \n"
+							  "     WHEN 'f' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\", \n"
+							  "CASE WHEN esd.stxdmcv IS NOT NULL THEN \n"
+							  "          pg_catalog.pg_size_pretty(pg_catalog.length(esd.stxdmcv)::bigint) \n"
+							  "     WHEN 'm' = any(es.stxkind) THEN '0 bytes' \n"
+							  "END AS \"%s\"",
+							  gettext_noop("Ndistinct_size"),
+							  gettext_noop("Dependencies_size"),
+							  gettext_noop("MCV_size"));
+		}
+	}
+
+	if (pset.sversion < 120000)
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+	else
+	{
+		appendPQExpBufferStr(&buf,
+							 " \nFROM pg_catalog.pg_statistic_ext es \n"
+							 "LEFT JOIN pg_catalog.pg_statistic_ext_data esd \n"
+							 "ON es.oid = esd.stxoid \n"
+							 "INNER JOIN pg_catalog.pg_class c \n"
+							 "ON es.stxrelid = c.oid \n");
+	}
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "es.stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..867e57d851 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern, bool verbose);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..e42bc8c54e 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX[+]  [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..611f1efb15 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1500,7 +1500,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3910,6 +3910,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

0002-regression-test-for-psql-dX-20210113.patchtext/plain; charset=UTF-8; name=0002-regression-test-for-psql-dX-20210113.patchDownload
From 5542fb4bb93df99dc7d0cc91489016f3609aff02 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Wed, 13 Jan 2021 09:51:39 +0900
Subject: [PATCH 2/2] regression test for psql \dX

---
 src/test/regress/expected/stats_ext.out | 94 +++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      | 31 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 7bfeaf85f0..b3ae4de185 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1725,6 +1725,100 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                           List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    
+----------+------------------------+--------------------------------------+-----------+--------------+-----------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built
+(12 rows)
+
+\dX stts_?
+                        List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    
+--------+--------+-------------------+-----------+--------------+-----------
+ public | stts_1 | a, b FROM stts_t1 | built     |              | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested
+(4 rows)
+
+\dX *stts_hoge
+                                List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    
+--------+-----------+-------------------------------+-----------+--------------+-----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested
+(1 row)
+
+\dX+
+                                                                    List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+----------+------------------------+--------------------------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | built        |           |                | 106 bytes         | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | built     |                |                   | 24 kB
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | built     |                |                   | 386 bytes
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | built     |                |                   | 294 bytes
+ public   | stts_1                 | a, b FROM stts_t1                    | built     |              |           | 13 bytes       |                   | 
+ public   | stts_2                 | a, b FROM stts_t1                    | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public   | stts_3                 | a, b FROM stts_t1                    | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public   | stts_4                 | b, c FROM stts_t2                    | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | requested    | requested |                | 0 bytes           | 0 bytes
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | built     |                |                   | 686 bytes
+(12 rows)
+
+\dX+ stts_?
+                                                 List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size |  MCV_size  
+--------+--------+-------------------+-----------+--------------+-----------+----------------+-------------------+------------
+ public | stts_1 | a, b FROM stts_t1 | built     |              |           | 13 bytes       |                   | 
+ public | stts_2 | a, b FROM stts_t1 | built     | built        |           | 13 bytes       | 40 bytes          | 
+ public | stts_3 | a, b FROM stts_t1 | built     | built        | built     | 13 bytes       | 40 bytes          | 6126 bytes
+ public | stts_4 | b, c FROM stts_t2 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+(4 rows)
+
+\dX+ *stts_hoge
+                                                        List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+--------+-----------+-------------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | requested | requested    | requested | 0 bytes        | 0 bytes           | 0 bytes
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                                                     List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |    MCV    | Ndistinct_size | Dependencies_size | MCV_size 
+---------+-----------+-------------------------+-----------+--------------+-----------+----------------+-------------------+----------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | requested    | requested |                | 0 bytes           | 0 bytes
+(1 row)
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 7912e733ae..db6e3e1ba3 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -912,6 +912,37 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

app-psql.htmltext/html; name=app-psql.htmlDownload
#58Julien Rouhaud
rjuju123@gmail.com
In reply to: Tatsuro Yamada (#57)
Re: list of extended statistics on psql

On Wed, Jan 13, 2021 at 10:22:05AM +0900, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/13 7:48, Tatsuro Yamada wrote:

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

��- "defined": it shows the extended stats defined only. We can't know
�������������� whether it needs to analyze or not. I agree this name was
��������������� ambiguous. Therefore we should replace it with a more suitable
�������������� name.
��- "requested": it shows the extended stats needs something. Of course,
�������������� we know it needs to ANALYZE because we can create the patch.
�������������� However, I feel there is a little ambiguity for DBA.
�������������� To solve this, it would be better to write an explanation of
�������������� the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
��was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

I updated the patch to add the explanation of the extended stats' statuses.
Please feel free to modify the patch to improve it more clearly.

The attached files are:
0001: Add psql \dx and the fixed document
0002: Regression test for psql \dX
app-psql.html: Created by "make html" command (You can check the
explanation of the statuses easily, probably)

Hello Yamada-san,

I reviewed the patch and don't have specific complaints, it all looks good!

I'm however thinking about the "requested" status. I'm wondering if it could
lead to people think that an ANALYZE is scheduled and will happen soon.
Maybe "defined" or "declared" might be less misleading, or even "waiting for
analyze"?

#59Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Julien Rouhaud (#58)
Re: list of extended statistics on psql

On 1/15/21 9:47 AM, Julien Rouhaud wrote:

On Wed, Jan 13, 2021 at 10:22:05AM +0900, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/13 7:48, Tatsuro Yamada wrote:

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

  - "defined": it shows the extended stats defined only. We can't know
               whether it needs to analyze or not. I agree this name was
                ambiguous. Therefore we should replace it with a more suitable
               name.
  - "requested": it shows the extended stats needs something. Of course,
               we know it needs to ANALYZE because we can create the patch.
               However, I feel there is a little ambiguity for DBA.
               To solve this, it would be better to write an explanation of
               the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
  was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

I updated the patch to add the explanation of the extended stats' statuses.
Please feel free to modify the patch to improve it more clearly.

The attached files are:
0001: Add psql \dx and the fixed document
0002: Regression test for psql \dX
app-psql.html: Created by "make html" command (You can check the
explanation of the statuses easily, probably)

Hello Yamada-san,

I reviewed the patch and don't have specific complaints, it all looks good!

I'm however thinking about the "requested" status. I'm wondering if it could
lead to people think that an ANALYZE is scheduled and will happen soon.
Maybe "defined" or "declared" might be less misleading, or even "waiting for
analyze"?

Well, the "defined" option is not great either, because it can be
interpreted as "NOT NULL" - that's why I proposed "requested". Not sure
about "declared" - I wouldn't use it in this context, but I'm not a
native speaker so maybe it's OK.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#60Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tomas Vondra (#59)
Re: list of extended statistics on psql

On 1/15/21 5:19 PM, Tomas Vondra wrote:

On 1/15/21 9:47 AM, Julien Rouhaud wrote:

On Wed, Jan 13, 2021 at 10:22:05AM +0900, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/13 7:48, Tatsuro Yamada wrote:

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you agree, or
do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

  - "defined": it shows the extended stats defined only. We can't know
               whether it needs to analyze or not. I agree this name was
                ambiguous. Therefore we should replace it with a more suitable
               name.
  - "requested": it shows the extended stats needs something. Of course,
               we know it needs to ANALYZE because we can create the patch.
               However, I feel there is a little ambiguity for DBA.
               To solve this, it would be better to write an explanation of
               the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE. "built" means ANALYZE
  was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

I updated the patch to add the explanation of the extended stats' statuses.
Please feel free to modify the patch to improve it more clearly.

The attached files are:
0001: Add psql \dx and the fixed document
0002: Regression test for psql \dX
app-psql.html: Created by "make html" command (You can check the
explanation of the statuses easily, probably)

Hello Yamada-san,

I reviewed the patch and don't have specific complaints, it all looks good!

I'm however thinking about the "requested" status. I'm wondering if it could
lead to people think that an ANALYZE is scheduled and will happen soon.
Maybe "defined" or "declared" might be less misleading, or even "waiting for
analyze"?

Well, the "defined" option is not great either, because it can be
interpreted as "NOT NULL" - that's why I proposed "requested". Not sure
about "declared" - I wouldn't use it in this context, but I'm not a
native speaker so maybe it's OK.

I've pushed this, keeping the "requested". If we decide that some other
term is a better choice, we can tweak that later of course.

Thanks Tatsuro-san for the patience!

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#61Shinoda, Noriyoshi (PN Japan FSIP)
noriyoshi.shinoda@hpe.com
In reply to: Tomas Vondra (#60)
RE: list of extended statistics on psql

Hi, hackers.

I tested this committed feature.
It doesn't seem to be available to non-superusers due to the inability to access pg_statistics_ext_data.
Is this the expected behavior?

--- operation ---
postgres=> CREATE STATISTICS stat1_data1 ON c1, c2 FROM data1;
CREATE STATISTICS
postgres=> ANALYZE data1;
ANALYZE
postgres=> SELECT * FROM pg_statistic_ext;
  oid  | stxrelid |   stxname   | stxnamespace | stxowner | stxstattarget | stxkeys | stxkind
-------+----------+-------------+--------------+----------+---------------+---------+---------
 16393 |    16385 | stat1_data1 |         2200 |    16384 |            -1 | 1 2     | {d,f,m}
(1 row)

postgres=> \dX
ERROR: permission denied for table pg_statistic_ext_data
postgres=>
postgres=> \connect postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=#
postgres=# \dX
List of extended statistics
Schema | Name | Definition | Ndistinct | Dependencies | MCV
--------+-------------+-------------------+-----------+--------------+-----------
public | stat1_data1 | c1, c2 FROM data1 | built | built | requested
(1 row)

--- operation ---

Regards,
Noriyoshi Shinoda

-----Original Message-----
From: Tomas Vondra [mailto:tomas.vondra@enterprisedb.com]
Sent: Sunday, January 17, 2021 8:32 AM
To: Julien Rouhaud <rjuju123@gmail.com>; Tatsuro Yamada <tatsuro.yamada.tf@nttcom.co.jp>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Tomas Vondra <tomas.vondra@2ndquadrant.com>; Michael Paquier <michael@paquier.xyz>; Pavel Stehule <pavel.stehule@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: list of extended statistics on psql

On 1/15/21 5:19 PM, Tomas Vondra wrote:

On 1/15/21 9:47 AM, Julien Rouhaud wrote:

On Wed, Jan 13, 2021 at 10:22:05AM +0900, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/13 7:48, Tatsuro Yamada wrote:

On 2021/01/12 20:08, Tomas Vondra wrote:

On 1/12/21 2:57 AM, Tatsuro Yamada wrote:

On 2021/01/09 9:01, Tomas Vondra wrote:

...>

While working on that, I realized that 'defined' might be a bit
ambiguous, I initially thought it means 'NOT NULL' (which it does not).
I propose to change it to 'requested' instead. Tatsuro, do you
agree, or do you think 'defined' is better?

Regarding the status of extended stats, I think the followings:

  - "defined": it shows the extended stats defined only. We
can't know
               whether it needs to analyze or not. I agree this
name was
                ambiguous. Therefore we should replace it with a
more suitable
               name.
  - "requested": it shows the extended stats needs something. Of
course,
               we know it needs to ANALYZE because we can create the patch.
               However, I feel there is a little ambiguity for DBA.
               To solve this, it would be better to write an
explanation of
               the status in the document. For example,

======
The column of the kind of extended stats (e. g. Ndistinct) shows some statuses.
"requested" means that it needs to gather data by ANALYZE.
"built" means ANALYZE
  was finished, and the planner can use it. NULL means that it doesn't exists.
======

What do you think? :-D

Yes, that seems reasonable to me. Will you provide an updated patch?

Sounds good. I'll send the updated patch today.

I updated the patch to add the explanation of the extended stats' statuses.
Please feel free to modify the patch to improve it more clearly.

The attached files are:
0001: Add psql \dx and the fixed document
0002: Regression test for psql \dX
app-psql.html: Created by "make html" command (You can check the
explanation of the statuses easily, probably)

Hello Yamada-san,

I reviewed the patch and don't have specific complaints, it all looks good!

I'm however thinking about the "requested" status. I'm wondering if
it could lead to people think that an ANALYZE is scheduled and will happen soon.
Maybe "defined" or "declared" might be less misleading, or even
"waiting for analyze"?

Well, the "defined" option is not great either, because it can be
interpreted as "NOT NULL" - that's why I proposed "requested". Not
sure about "declared" - I wouldn't use it in this context, but I'm not
a native speaker so maybe it's OK.

I've pushed this, keeping the "requested". If we decide that some other term is a better choice, we can tweak that later of course.

Thanks Tatsuro-san for the patience!

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#62Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Shinoda, Noriyoshi (PN Japan FSIP) (#61)
Re: list of extended statistics on psql

On 1/17/21 2:41 AM, Shinoda, Noriyoshi (PN Japan FSIP) wrote:

Hi, hackers.

I tested this committed feature.
It doesn't seem to be available to non-superusers due to the inability to access pg_statistics_ext_data.
Is this the expected behavior?

Hmmm, that's a good point. Bummer we haven't noticed that earlier.

I wonder what the right fix should be - presumably we could do something
like pg_stats_ext (we can't use that view directly, because it formats
the data, so the sizes are different).

But should it list just the stats the user has access to, or should it
list everything and leave the inaccessible fields NULL?

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#63Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tomas Vondra (#62)
Re: list of extended statistics on psql

On 1/17/21 3:01 AM, Tomas Vondra wrote:

On 1/17/21 2:41 AM, Shinoda, Noriyoshi (PN Japan FSIP) wrote:

Hi, hackers.

I tested this committed feature.
It doesn't seem to be available to non-superusers due to the inability
to access pg_statistics_ext_data.
Is this the expected behavior?

Hmmm, that's a good point. Bummer we haven't noticed that earlier.

I wonder what the right fix should be - presumably we could do something
like pg_stats_ext (we can't use that view directly, because it formats
the data, so the sizes are different).

But should it list just the stats the user has access to, or should it
list everything and leave the inaccessible fields NULL?

I've reverted the commit - once we find the right way to handle this,
I'll get it committed again.

As for how to deal with this, I can think of about three ways:

1) simplify the command to only print information from pg_statistic_ext
(so on information about which stats are built or sizes)

2) extend pg_stats_ext with necessary information (e.g. sizes)

3) create a new system view, with necessary information (so that
pg_stats_ext does not need to be modified)

4) add functions returning the necessary information, possibly only for
statistics the user can access (similarly to what pg_stats_ext does)

Options 2-4 have the obvious disadvantage that this won't work on older
releases (we can't add views or functions there). So I'm leaning towards
#1 even if that means we have to remove some of the details. We can
consider adding that for new releases, though.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#64Justin Pryzby
pryzby@telsasoft.com
In reply to: Tomas Vondra (#63)
Re: list of extended statistics on psql

On Sun, Jan 17, 2021 at 03:31:57PM +0100, Tomas Vondra wrote:

I've reverted the commit - once we find the right way to handle this, I'll
get it committed again.

Please consider these doc changes for the next iteration.

commit 1a69f648ce6c63ebb37b6d8ec7c6539b3cb70787
Author: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat Jan 16 17:47:35 2021 -0600

doc review: psql \dX 891a1d0bca262ca78564e0fea1eaa5ae544ea5ee

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index aaf55df921..a678a69dfb 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1928,15 +1928,15 @@ testdb=&gt;
         is specified, only those extended statistics whose names match the
         pattern are listed.
         If <literal>+</literal> is appended to the command name, each extended
-        statistics is listed with its size.
+        statistic is listed with its size.
         </para>
         <para>
-        The column of the kind of extended stats (e.g. Ndistinct) shows some statuses.
+        The column of the kind of extended stats (e.g. Ndistinct) shows its status.
         "requested" means that it needs to collect statistics by <link
         linkend="sql-analyze"><command>ANALYZE</command></link>. 
         "built" means <link linkend="sql-analyze"><command>ANALYZE</command></link> was 
-        finished, and the planner can use it. NULL means that it doesn't exists. 
+        run, and statistics are available to the planner. NULL means that it doesn't exist. 
         </para>
         </listitem>
       </varlistentry>
#65Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Julien Rouhaud (#58)
Re: list of extended statistics on psql

Hi Julien,

On 2021/01/15 17:47, Julien Rouhaud wrote:

Hello Yamada-san,

I reviewed the patch and don't have specific complaints, it all looks good!

I'm however thinking about the "requested" status. I'm wondering if it could
lead to people think that an ANALYZE is scheduled and will happen soon.
Maybe "defined" or "declared" might be less misleading, or even "waiting for
analyze"?

Thanks for reviewing the patch.
Yeah, "waiting for analyze" was preferable but it was a little long to use on the columns. Unfortunately, I couldn't imagine a suitable term. Therefore,
I'm keeping it as is.

Regards,
Tatsuro Yamada

#66Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#60)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/17 8:32, Tomas Vondra wrote:

I've pushed this, keeping the "requested". If we decide that some other term is a better choice, we can tweak that later of course.

Thanks Tatsuro-san for the patience!

Thanks for taking the time to review the patches.
I believe this feature is useful for DBA when they use Extended stats.
For example, the execution plan tuning phase and so on.

Then, I know the patch was reverted. So, I keep going to fix the patch
on the Second iteration. :-D

Regards,
Tatsuro Yamada

#67Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#63)
Re: list of extended statistics on psql

Hi Tomas and Shinoda-san,

On 2021/01/17 23:31, Tomas Vondra wrote:

On 1/17/21 3:01 AM, Tomas Vondra wrote:

On 1/17/21 2:41 AM, Shinoda, Noriyoshi (PN Japan FSIP) wrote:

Hi, hackers.

I tested this committed feature.
It doesn't seem to be available to non-superusers due to the inability to access pg_statistics_ext_data.
Is this the expected behavior?

Ugh. I overlooked the test to check the case of the user hasn't Superuser privilege. The user without the privilege was able to access pg_statistics_ext. Therefore I supposed that it's also able to access pg_statics_ext_data. Oops.

Hmmm, that's a good point. Bummer we haven't noticed that earlier.

I wonder what the right fix should be - presumably we could do something like pg_stats_ext (we can't use that view directly, because it formats the data, so the sizes are different).

But should it list just the stats the user has access to, or should it list everything and leave the inaccessible fields NULL?

I've reverted the commit - once we find the right way to handle this, I'll get it committed again.

As for how to deal with this, I can think of about three ways:

1) simplify the command to only print information from pg_statistic_ext (so on information about which stats are built or sizes)

2) extend pg_stats_ext with necessary information (e.g. sizes)

3) create a new system view, with necessary information (so that pg_stats_ext does not need to be modified)

4) add functions returning the necessary information, possibly only for statistics the user can access (similarly to what pg_stats_ext does)

Options 2-4 have the obvious disadvantage that this won't work on older releases (we can't add views or functions there). So I'm leaning towards #1 even if that means we have to remove some of the details. We can consider adding that for new releases, though.

Thanks for the useful advice. I go with option 1).
The following query is created by using pg_stats_ext instead of pg_statistic_ext and pg_statistic_ext_data. However, I was confused
about writing a part of the query for calculating MCV size because
there are four columns related to MCV. For example, most_common_vals, most_common_val_nulls, most_common_freqs, and most_common_base_freqs.
Currently, I don't know how to calculate the size of MCV by using the
four columns. Thoughts? :-)

===================================================
\connect postgres hoge
create table hoge_t(a int, b int);
insert into hoge_t select i,i from generate_series(1,100) i;
create statistics hoge_t_ext on a, b from hoge_t;

SELECT
es.statistics_schemaname AS "Schema",
es.statistics_name AS "Name",
pg_catalog.format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(s.attname),', ')
FROM pg_catalog.unnest(es.attnames) s(attname)),
es.tablename) AS "Definition",
CASE WHEN es.n_distinct IS NOT NULL THEN 'built'
WHEN 'd' = any(es.kinds) THEN 'requested'
END AS "Ndistinct",
CASE WHEN es.dependencies IS NOT NULL THEN 'built'
WHEN 'f' = any(es.kinds) THEN 'requested'
END AS "Dependencies",
CASE WHEN es.most_common_vals IS NOT NULL THEN 'built'
WHEN 'm' = any(es.kinds) THEN 'requested'
END AS "MCV",
CASE WHEN es.n_distinct IS NOT NULL THEN
pg_catalog.pg_size_pretty(pg_catalog.length(es.n_distinct)::bigint)
WHEN 'd' = any(es.kinds) THEN '0 bytes'
END AS "Ndistinct_size",
CASE WHEN es.dependencies IS NOT NULL THEN
pg_catalog.pg_size_pretty(pg_catalog.length(es.dependencies)::bigint)
WHEN 'f' = any(es.kinds) THEN '0 bytes'
END AS "Dependencies_size"
FROM pg_catalog.pg_stats_ext es
ORDER BY 1, 2;

-[ RECORD 1 ]-----+-----------------
Schema | public
Name | hoge_t_ext
Definition | a, b FROM hoge_t
Ndistinct | requested
Dependencies | requested
MCV | requested
Ndistinct_size | 0 bytes
Dependencies_size | 0 bytes

analyze hoge_t;

-[ RECORD 1 ]-----+-----------------
Schema | public
Name | hoge_t_ext
Definition | a, b FROM hoge_t
Ndistinct | built
Dependencies | built
MCV | built
Ndistinct_size | 13 bytes
Dependencies_size | 40 bytes
===================================================

Thanks,
Tatsuro Yamada

#68Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Justin Pryzby (#64)
Re: list of extended statistics on psql

Hi Justin,

On 2021/01/18 1:52, Justin Pryzby wrote:

On Sun, Jan 17, 2021 at 03:31:57PM +0100, Tomas Vondra wrote:

I've reverted the commit - once we find the right way to handle this, I'll
get it committed again.

Please consider these doc changes for the next iteration.

commit 1a69f648ce6c63ebb37b6d8ec7c6539b3cb70787
Author: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat Jan 16 17:47:35 2021 -0600

doc review: psql \dX 891a1d0bca262ca78564e0fea1eaa5ae544ea5ee

Thanks for your comments!
It helps a lot since I'm not a native speaker.
I'll fix the document based on your suggestion on the next patch.

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index aaf55df921..a678a69dfb 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1928,15 +1928,15 @@ testdb=&gt;
is specified, only those extended statistics whose names match the
pattern are listed.
If <literal>+</literal> is appended to the command name, each extended
-        statistics is listed with its size.
+        statistic is listed with its size.

Agreed.

<para>
-        The column of the kind of extended stats (e.g. Ndistinct) shows some statuses.
+        The column of the kind of extended stats (e.g. Ndistinct) shows its status.
"requested" means that it needs to collect statistics by <link
linkend="sql-analyze"><command>ANALYZE</command></link>.
"built" means <link linkend="sql-analyze"><command>ANALYZE</command></link> was

Agreed.

-        finished, and the planner can use it. NULL means that it doesn't exists.
+        run, and statistics are available to the planner. NULL means that it doesn't exist.

Agreed.

Thanks,
Tatsuro Yamada

#69Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#67)
Re: list of extended statistics on psql

On 1/18/21 8:31 AM, Tatsuro Yamada wrote:

Hi Tomas and Shinoda-san,

On 2021/01/17 23:31, Tomas Vondra wrote:

On 1/17/21 3:01 AM, Tomas Vondra wrote:

On 1/17/21 2:41 AM, Shinoda, Noriyoshi (PN Japan FSIP) wrote:

Hi, hackers.

I tested this committed feature.
It doesn't seem to be available to non-superusers due to the
inability to access pg_statistics_ext_data.
Is this the expected behavior?

Ugh. I overlooked the test to check the case of the user hasn't
Superuser privilege. The user without the privilege was able to access
pg_statistics_ext. Therefore I supposed that it's also able to access
pg_statics_ext_data. Oops.

Hmmm, that's a good point. Bummer we haven't noticed that earlier.

I wonder what the right fix should be - presumably we could do
something like pg_stats_ext (we can't use that view directly, because
it formats the data, so the sizes are different).

But should it list just the stats the user has access to, or should
it list everything and leave the inaccessible fields NULL?

I've reverted the commit - once we find the right way to handle this,
I'll get it committed again.

As for how to deal with this, I can think of about three ways:

1) simplify the command to only print information from
pg_statistic_ext (so on information about which stats are built or sizes)

2) extend pg_stats_ext with necessary information (e.g. sizes)

3) create a new system view, with necessary information (so that
pg_stats_ext does not need to be modified)

4) add functions returning the necessary information, possibly only
for statistics the user can access (similarly to what pg_stats_ext does)

Options 2-4 have the obvious disadvantage that this won't work on
older releases (we can't add views or functions there). So I'm leaning
towards #1 even if that means we have to remove some of the details.
We can consider adding that for new releases, though.

Thanks for the useful advice. I go with option 1).
The following query is created by using pg_stats_ext instead of
pg_statistic_ext and pg_statistic_ext_data. However, I was confused
about writing a part of the query for calculating MCV size because
there are four columns related to MCV. For example, most_common_vals,
most_common_val_nulls, most_common_freqs, and most_common_base_freqs.
Currently, I don't know how to calculate the size of MCV by using the
four columns. Thoughts? :-)

Well, my suggestion was to use pg_statistic_ext, because that lists all
statistics, while pg_stats_ext is filtering statistics depending on
access privileges. I think that's more appropriate for \dX, the contents
should not change depending on the user.

Also, let me clarify - with option (1) we'd not show the sizes at all.
The size of the formatted statistics may be very different from the
on-disk representation, so I see no point in showing it in \dX.

We might show other stats (e.g. number of MCV items, or the fraction of
data represented by the MCV list), but the user can inspect pg_stats_ext
if needed.

What we might do is to show those stats when a superuser is running this
command, but I'm not sure that's a good idea (or how difficult would it
be to implement).

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#70Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#69)
Re: list of extended statistics on psql

Hi Tomas,

As for how to deal with this, I can think of about three ways:

1) simplify the command to only print information from pg_statistic_ext (so on information about which stats are built or sizes)

2) extend pg_stats_ext with necessary information (e.g. sizes)

3) create a new system view, with necessary information (so that pg_stats_ext does not need to be modified)

4) add functions returning the necessary information, possibly only for statistics the user can access (similarly to what pg_stats_ext does)

Options 2-4 have the obvious disadvantage that this won't work on older releases (we can't add views or functions there). So I'm leaning towards #1 even if that means we have to remove some of the details. We can consider adding that for new releases, though.

Thanks for the useful advice. I go with option 1).
The following query is created by using pg_stats_ext instead of pg_statistic_ext and pg_statistic_ext_data. However, I was confused
about writing a part of the query for calculating MCV size because
there are four columns related to MCV. For example, most_common_vals, most_common_val_nulls, most_common_freqs, and most_common_base_freqs.
Currently, I don't know how to calculate the size of MCV by using the
four columns. Thoughts? :-)

Well, my suggestion was to use pg_statistic_ext, because that lists all statistics, while pg_stats_ext is filtering statistics depending on access privileges. I think that's more appropriate for \dX, the contents should not change depending on the user.

Also, let me clarify - with option (1) we'd not show the sizes at all. The size of the formatted statistics may be very different from the on-disk representation, so I see no point in showing it in \dX.

We might show other stats (e.g. number of MCV items, or the fraction of data represented by the MCV list), but the user can inspect pg_stats_ext if needed.

What we might do is to show those stats when a superuser is running this command, but I'm not sure that's a good idea (or how difficult would it be to implement).

Thanks for clarifying.
I see that your suggestion was to use pg_statistic_ext, not pg_stats_ext.
And we don't need the size of stats.

If that's the case, we also can't get the status of stats since PG12 or later
because we can't use pg_statistic_ext_data, as you know. Therefore, it would be
better to replace the query with the old query that I sent five months ago like this:

# the old query
SELECT
stxnamespace::pg_catalog.regnamespace AS "Schema",
stxrelid::pg_catalog.regclass AS "Table",
stxname AS "Name",
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
FROM pg_catalog.unnest(stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND
a.attnum = s.attnum AND NOT attisdropped)) AS "Columns",
'd' = any(stxkind) AS "Ndistinct",
'f' = any(stxkind) AS "Dependencies",
'm' = any(stxkind) AS "MCV"
FROM pg_catalog.pg_statistic_ext stat
ORDER BY 1,2;

Schema | Table | Name | Columns | Ndistinct | Dependencies | MCV
--------+--------+------------+---------+-----------+--------------+-----
public | hoge1 | hoge1_ext | a, b | t | t | t
public | hoge_t | hoge_t_ext | a, b | t | t | t
(2 rows)

The above query is so simple so that we would better to use the following query:

# This query works on PG10 or later
SELECT
es.stxnamespace::pg_catalog.regnamespace::text AS "Schema",
es.stxname AS "Name",
pg_catalog.format('%s FROM %s',
(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')
FROM pg_catalog.unnest(es.stxkeys) s(attnum)
JOIN pg_catalog.pg_attribute a
ON (es.stxrelid = a.attrelid
AND a.attnum = s.attnum
AND NOT a.attisdropped)),
es.stxrelid::regclass) AS "Definition",
CASE WHEN 'd' = any(es.stxkind) THEN 'defined'
END AS "Ndistinct",
CASE WHEN 'f' = any(es.stxkind) THEN 'defined'
END AS "Dependencies",
CASE WHEN 'm' = any(es.stxkind) THEN 'defined'
END AS "MCV"
FROM pg_catalog.pg_statistic_ext es
ORDER BY 1, 2;

Schema | Name | Definition | Ndistinct | Dependencies | Dependencies
--------+------------+------------------+-----------+--------------+--------------
public | hoge1_ext | a, b FROM hoge1 | defined | defined | defined
public | hoge_t_ext | a, b FROM hoge_t | defined | defined | defined
(2 rows)

I'm going to create the WIP patch to use the above queriy.
Any comments welcome. :-D

Thanks,
Tatsuro Yamada

#71Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#70)
Re: list of extended statistics on psql

On 1/19/21 1:44 AM, Tatsuro Yamada wrote:

Hi Tomas,

As for how to deal with this, I can think of about three ways:

1) simplify the command to only print information from
pg_statistic_ext (so on information about which stats are built or
sizes)

2) extend pg_stats_ext with necessary information (e.g. sizes)

3) create a new system view, with necessary information (so that
pg_stats_ext does not need to be modified)

4) add functions returning the necessary information, possibly only
for statistics the user can access (similarly to what pg_stats_ext
does)

Options 2-4 have the obvious disadvantage that this won't work on
older releases (we can't add views or functions there). So I'm
leaning towards #1 even if that means we have to remove some of the
details. We can consider adding that for new releases, though.

Thanks for the useful advice. I go with option 1).
The following query is created by using pg_stats_ext instead of
pg_statistic_ext and pg_statistic_ext_data. However, I was confused
about writing a part of the query for calculating MCV size because
there are four columns related to MCV. For example, most_common_vals,
most_common_val_nulls, most_common_freqs, and most_common_base_freqs.
Currently, I don't know how to calculate the size of MCV by using the
four columns. Thoughts? :-)

Well, my suggestion was to use pg_statistic_ext, because that lists
all statistics, while pg_stats_ext is filtering statistics depending
on access privileges. I think that's more appropriate for \dX, the
contents should not change depending on the user.

Also, let me clarify - with option (1) we'd not show the sizes at all.
The size of the formatted statistics may be very different from the
on-disk representation, so I see no point in showing it in \dX.

We might show other stats (e.g. number of MCV items, or the fraction
of data represented by the MCV list), but the user can inspect
pg_stats_ext if needed.

What we might do is to show those stats when a superuser is running
this command, but I'm not sure that's a good idea (or how difficult
would it be to implement).

Thanks for clarifying.
I see that your suggestion was to use pg_statistic_ext, not pg_stats_ext.
And we don't need the size of stats.

If that's the case, we also can't get the status of stats since PG12 or
later
because we can't use pg_statistic_ext_data, as you know. Therefore, it
would be
better to replace the query with the old query that I sent five months
ago like this:

# the old query
SELECT
    stxnamespace::pg_catalog.regnamespace AS "Schema",
    stxrelid::pg_catalog.regclass AS "Table",
    stxname AS "Name",
    (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')
     FROM pg_catalog.unnest(stxkeys) s(attnum)
     JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND
     a.attnum = s.attnum AND NOT attisdropped)) AS "Columns",
    'd' = any(stxkind) AS "Ndistinct",
    'f' = any(stxkind) AS "Dependencies",
    'm' = any(stxkind) AS "MCV"
FROM pg_catalog.pg_statistic_ext stat
ORDER BY 1,2;

 Schema | Table  |    Name    | Columns | Ndistinct | Dependencies | MCV
--------+--------+------------+---------+-----------+--------------+-----
 public | hoge1  | hoge1_ext  | a, b    | t         | t            | t
 public | hoge_t | hoge_t_ext | a, b    | t         | t            | t
(2 rows)

The above query is so simple so that we would better to use the
following query:

# This query works on PG10 or later
SELECT
    es.stxnamespace::pg_catalog.regnamespace::text AS "Schema",
    es.stxname AS "Name",
    pg_catalog.format('%s FROM %s',
        (SELECT
pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')
         FROM pg_catalog.unnest(es.stxkeys) s(attnum)
         JOIN pg_catalog.pg_attribute a
         ON (es.stxrelid = a.attrelid
         AND a.attnum = s.attnum
         AND NOT a.attisdropped)),
    es.stxrelid::regclass) AS "Definition",
    CASE WHEN 'd' = any(es.stxkind) THEN 'defined'
    END AS "Ndistinct",
    CASE WHEN 'f' = any(es.stxkind) THEN 'defined'
    END AS "Dependencies",
    CASE WHEN 'm' = any(es.stxkind) THEN 'defined'
    END AS "MCV"
FROM pg_catalog.pg_statistic_ext es
ORDER BY 1, 2;

 Schema |    Name    |    Definition    | Ndistinct | Dependencies |
Dependencies
--------+------------+------------------+-----------+--------------+--------------

 public | hoge1_ext  | a, b FROM hoge1  | defined   | defined      |
defined
 public | hoge_t_ext | a, b FROM hoge_t | defined   | defined      |
defined
(2 rows)

I'm going to create the WIP patch to use the above queriy.
Any comments welcome. :-D

Yes, I think using this simpler query makes sense. If we decide we need
something more elaborate, we can improve that by in future PostgreSQL
versions (after adding view/function to core), but I'd leave that as a
work for the future.

Apologies for all the extra work - I haven't realized this flaw when
pushing for showing more stuff :-(

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#72Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#70)
1 attachment(s)
Re: list of extended statistics on psql

Hi,

The above query is so simple so that we would better to use the following query:

# This query works on PG10 or later
SELECT
    es.stxnamespace::pg_catalog.regnamespace::text AS "Schema",
    es.stxname AS "Name",
    pg_catalog.format('%s FROM %s',
        (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')
         FROM pg_catalog.unnest(es.stxkeys) s(attnum)
         JOIN pg_catalog.pg_attribute a
         ON (es.stxrelid = a.attrelid
         AND a.attnum = s.attnum
         AND NOT a.attisdropped)),
    es.stxrelid::regclass) AS "Definition",
    CASE WHEN 'd' = any(es.stxkind) THEN 'defined'
    END AS "Ndistinct",
    CASE WHEN 'f' = any(es.stxkind) THEN 'defined'
    END AS "Dependencies",
    CASE WHEN 'm' = any(es.stxkind) THEN 'defined'
    END AS "MCV"
FROM pg_catalog.pg_statistic_ext es
ORDER BY 1, 2;

 Schema |    Name    |    Definition    | Ndistinct | Dependencies | Dependencies
--------+------------+------------------+-----------+--------------+--------------
 public | hoge1_ext  | a, b FROM hoge1  | defined   | defined      | defined
 public | hoge_t_ext | a, b FROM hoge_t | defined   | defined      | defined
(2 rows)

I'm going to create the WIP patch to use the above query.
Any comments welcome. :-D

Attached patch is WIP patch.

The changes are:
- Use pg_statistic_ext only
- Remove these statuses: "required" and "built"
- Add new status: "defined"
- Remove the size columns
- Fix document

I'll create and send the regression test on the next patch if there is
no objection. Is it Okay?

Regards,
Tatsuro Yamada

Attachments:

WIP_psql_dX_using_pg_statistic_ext.patchtext/plain; charset=UTF-8; name=WIP_psql_dX_using_pg_statistic_ext.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..36a79d9e3f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,26 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the
+        pattern are listed.
+        </para>
+
+        <para>
+        The column of the kind of extended stats (e.g. Ndistinct) shows its status.
+        NULL means that it doesn't exists. "defined" means that it is declared.
+        You can use pg_stats_ext if you'd like to know whether <link linkend="sql-analyze">
+        <command>ANALYZE</command></link> was run and statistics are available to the
+        planner.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c98e3d31d0 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index caf97563f4..899fe5d85c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4392,6 +4392,89 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	appendPQExpBuffer(&buf,
+					  ",\nCASE WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"));
+
+	/*
+	 * Add the MCV statistics kind.
+	 */ 	
+	if (pset.sversion >= 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("MCV"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false,
+						  false, NULL,
+						  "es.stxname", NULL,
+						  NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..39856a0c7e 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..4883ebd2ed 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6abcbea963..17f7265038 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1505,7 +1505,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3974,6 +3974,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
#73Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#71)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/19 11:52, Tomas Vondra wrote:

I'm going to create the WIP patch to use the above queriy.
Any comments welcome. :-D

Yes, I think using this simpler query makes sense. If we decide we need something more elaborate, we can improve that by in future PostgreSQL versions (after adding view/function to core), but I'd leave that as a work for the future.

I see, thanks!

Apologies for all the extra work - I haven't realized this flaw when pushing for showing more stuff :-(

Don't worry about it. We didn't notice the problem even when viewed by multiple
people on -hackers. Let's keep moving forward. :-D

I'll send a patch including a regression test on the next patch.

Regards,
Tatsuro Yamada

#74Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tatsuro Yamada (#73)
2 attachment(s)
Re: list of extended statistics on psql

Hi Tomas,

On 2021/01/20 11:35, Tatsuro Yamada wrote:

Apologies for all the extra work - I haven't realized this flaw when pushing for showing more stuff :-(

Don't worry about it. We didn't notice the problem even when viewed by multiple
people on -hackers. Let's keep moving forward. :-D

I'll send a patch including a regression test on the next patch.

I created patches and my test results on PG10, 11, 12, and 14 are fine.

0001:
- Fix query to use pg_statistic_ext only
- Replace statuses "required" and "built" with "defined"
- Remove the size columns
- Fix document
- Add schema name as a filter condition on the query

0002:
- Fix all results of \dX
- Add new testcase by non-superuser

Please find attached files. :-D

Regards,
Tatsuro Yamada

Attachments:

0002-psql-dX-regression-test-20210120.patchtext/plain; charset=UTF-8; name=0002-psql-dX-regression-test-20210120.patchDownload
From 1aac3df2af2f6c834ffab10ddd1be1dee5970eb3 Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Wed, 20 Jan 2021 15:33:04 +0900
Subject: [PATCH 2/2] psql \dX regression test

Add a test by non-superuser
---
 src/test/regress/expected/stats_ext.out | 116 ++++++++++++++++++++++++++++++++
 src/test/regress/sql/stats_ext.sql      |  37 ++++++++++
 2 files changed, 153 insertions(+)

diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index f094731e32..0ff4e51055 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -1727,6 +1727,122 @@ INSERT INTO tststats.priv_test_tbl
 CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
   FROM tststats.priv_test_tbl;
 ANALYZE tststats.priv_test_tbl;
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | defined      | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | defined
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | defined
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | defined
+ public   | stts_1                 | a, b FROM stts_t1                    | defined   |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | defined   | defined      | 
+ public   | stts_3                 | a, b FROM stts_t1                    | defined   | defined      | defined
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | defined
+(12 rows)
+
+\dX stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | defined   |              | 
+ public | stts_2 | a, b FROM stts_t1 | defined   | defined      | 
+ public | stts_3 | a, b FROM stts_t1 | defined   | defined      | defined
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | defined      | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | defined
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | defined
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | defined
+ public   | stts_1                 | a, b FROM stts_t1                    | defined   |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | defined   | defined      | 
+ public   | stts_3                 | a, b FROM stts_t1                    | defined   | defined      | defined
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | defined
+(12 rows)
+
+\dX+ stts_?
+                       List of extended statistics
+ Schema |  Name  |    Definition     | Ndistinct | Dependencies |   MCV   
+--------+--------+-------------------+-----------+--------------+---------
+ public | stts_1 | a, b FROM stts_t1 | defined   |              | 
+ public | stts_2 | a, b FROM stts_t1 | defined   | defined      | 
+ public | stts_3 | a, b FROM stts_t1 | defined   | defined      | defined
+ public | stts_4 | b, c FROM stts_t2 | defined   | defined      | defined
+(4 rows)
+
+\dX+ *stts_hoge
+                               List of extended statistics
+ Schema |   Name    |          Definition           | Ndistinct | Dependencies |   MCV   
+--------+-----------+-------------------------------+-----------+--------------+---------
+ public | stts_hoge | col1, col2, col3 FROM stts_t3 | defined   | defined      | defined
+(1 row)
+
+\dX+ stts_s2.stts_yama
+                            List of extended statistics
+ Schema  |   Name    |       Definition        | Ndistinct | Dependencies |   MCV   
+---------+-----------+-------------------------+-----------+--------------+---------
+ stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined
+(1 row)
+
+create user non_superuser password 'hoge';
+\connect regression non_superuser
+\dX
+                                          List of extended statistics
+  Schema  |          Name          |              Definition              | Ndistinct | Dependencies |   MCV   
+----------+------------------------+--------------------------------------+-----------+--------------+---------
+ public   | func_deps_stat         | a, b, c FROM functional_dependencies |           | defined      | 
+ public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays        |           |              | defined
+ public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool          |           |              | defined
+ public   | mcv_lists_stats        | a, b, d FROM mcv_lists               |           |              | defined
+ public   | stts_1                 | a, b FROM stts_t1                    | defined   |              | 
+ public   | stts_2                 | a, b FROM stts_t1                    | defined   | defined      | 
+ public   | stts_3                 | a, b FROM stts_t1                    | defined   | defined      | defined
+ public   | stts_4                 | b, c FROM stts_t2                    | defined   | defined      | defined
+ public   | stts_hoge              | col1, col2, col3 FROM stts_t3        | defined   | defined      | defined
+ stts_s1  | stts_foo               | col1, col2 FROM stts_t3              | defined   | defined      | defined
+ stts_s2  | stts_yama              | col1, col3 FROM stts_t3              |           | defined      | defined
+ tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl     |           |              | defined
+(12 rows)
+
+\connect regression postgres
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+drop user non_superuser;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index cb08b478a4..a946c350bd 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -914,6 +914,43 @@ CREATE STATISTICS tststats.priv_test_stats (mcv) ON a, b
 
 ANALYZE tststats.priv_test_tbl;
 
+-- Check printing info about extended statistics by \dX
+create table stts_t1 (a int, b int);
+create statistics stts_1 (ndistinct) on a, b from stts_t1;
+create statistics stts_2 (ndistinct, dependencies) on a, b from stts_t1;
+create statistics stts_3 (ndistinct, dependencies, mcv) on a, b from stts_t1;
+
+create table stts_t2 (a int, b int, c int);
+create statistics stts_4 on b, c from stts_t2;
+
+create table stts_t3 (col1 int, col2 int, col3 int);
+create statistics stts_hoge on col1, col2, col3 from stts_t3;
+
+create schema stts_s1;
+create schema stts_s2;
+create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
+create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
+
+insert into stts_t1 select i,i from generate_series(1,100) i;
+analyze stts_t1;
+
+\dX
+\dX stts_?
+\dX *stts_hoge
+\dX+
+\dX+ stts_?
+\dX+ *stts_hoge
+\dX+ stts_s2.stts_yama
+
+create user non_superuser password 'hoge';
+\connect regression non_superuser
+\dX
+\connect regression postgres
+
+drop table stts_t1, stts_t2, stts_t3;
+drop schema stts_s1, stts_s2 cascade;
+drop user non_superuser;
+
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
-- 
2.16.5

0001-psql-dX-list-extended-statistics-objects-take2-20210120.patchtext/plain; charset=UTF-8; name=0001-psql-dX-list-extended-statistics-objects-take2-20210120.patchDownload
From e42b671f930765e16491f0dee5c1cfc1e57a9daa Mon Sep 17 00:00:00 2001
From: Tatsuro Yamada <yamatattsu@gmail.com>
Date: Wed, 20 Jan 2021 15:09:27 +0900
Subject: [PATCH 1/2] psql \dX: list extended statistics objects take2

The new command lists extended statistics objects.
All past releases with extended statistics are supported.

Author: Tatsuro Yamada
Reviewed-by: Julien Rouhaud, Alvaro Herrera, Tomas Vondra, Justin Pryzby
Discussion: https://postgr.es/m/c027a541-5856-75a5-0868-341301e1624b%40nttcom.co.jp_1
---
 doc/src/sgml/ref/psql-ref.sgml | 20 ++++++++++
 src/bin/psql/command.c         |  3 ++
 src/bin/psql/describe.c        | 83 ++++++++++++++++++++++++++++++++++++++++++
 src/bin/psql/describe.h        |  3 ++
 src/bin/psql/help.c            |  1 +
 src/bin/psql/tab-complete.c    |  4 +-
 6 files changed, 113 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 221a967bfe..36a79d9e3f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1918,6 +1918,26 @@ testdb=&gt;
         </para>
         </listitem>
       </varlistentry>
+      
+      <varlistentry>
+        <term><literal>\dX [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists extended statistics.
+        If <replaceable class="parameter">pattern</replaceable>
+        is specified, only those extended statistics whose names match the
+        pattern are listed.
+        </para>
+
+        <para>
+        The column of the kind of extended stats (e.g. Ndistinct) shows its status.
+        NULL means that it doesn't exists. "defined" means that it is declared.
+        You can use pg_stats_ext if you'd like to know whether <link linkend="sql-analyze">
+        <command>ANALYZE</command></link> was run and statistics are available to the
+        planner.
+        </para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><literal>\dy[+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 303e7c3ad8..c98e3d31d0 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -928,6 +928,9 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
 				else
 					success = listExtensions(pattern);
 				break;
+			case 'X':			/* Extended Statistics */
+				success = listExtendedStats(pattern);
+				break;
 			case 'y':			/* Event Triggers */
 				success = listEventTriggers(pattern, show_verbose);
 				break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index caf97563f4..95954fc319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4392,6 +4392,89 @@ listEventTriggers(const char *pattern, bool verbose)
 	return true;
 }
 
+/*
+ * \dX
+ *
+ * Describes extended statistics.
+ */
+bool
+listExtendedStats(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+
+	if (pset.sversion < 100000)
+	{
+		char		sverbuf[32];
+
+		pg_log_error("The server (version %s) does not support extended statistics.",
+					 formatPGVersionNumber(pset.sversion, false,
+										   sverbuf, sizeof(sverbuf)));
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+	printfPQExpBuffer(&buf,
+					  "SELECT \n"
+					  "es.stxnamespace::pg_catalog.regnamespace::text AS \"%s\", \n"
+					  "es.stxname AS \"%s\", \n"
+					  "pg_catalog.format('%%s FROM %%s', \n"
+					  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
+					  "   FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
+					  "   JOIN pg_catalog.pg_attribute a \n"
+					  "   ON (es.stxrelid = a.attrelid \n"
+					  "   AND a.attnum = s.attnum \n"
+					  "   AND NOT a.attisdropped)), \n"
+					  "es.stxrelid::regclass) AS \"%s\"",
+					  gettext_noop("Schema"),
+					  gettext_noop("Name"),
+					  gettext_noop("Definition"));
+
+	appendPQExpBuffer(&buf,
+					  ",\nCASE WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\", \n"
+					  "CASE WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
+					  "END AS \"%s\"",
+					  gettext_noop("Ndistinct"),
+					  gettext_noop("Dependencies"));
+
+	/*
+	 * Add the MCV statistics kind.
+	 */
+	if (pset.sversion >= 120000)
+	{
+		appendPQExpBuffer(&buf,
+						  ",\nCASE WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
+						  "END AS \"%s\" ",
+						  gettext_noop("MCV"));
+	}
+
+	appendPQExpBufferStr(&buf,
+						 " \nFROM pg_catalog.pg_statistic_ext es \n");
+
+	processSQLNamePattern(pset.db, &buf, pattern,
+						  false, false,
+						  "es.stxnamespace::pg_catalog.regnamespace::text", "es.stxname",
+						  NULL, NULL);
+
+	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	myopt.nullPrint = NULL;
+	myopt.title = _("List of extended statistics");
+	myopt.translate_header = true;
+
+	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+	PQclear(res);
+	return true;
+}
+
 /*
  * \dC
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 6044e3a082..39856a0c7e 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -102,6 +102,9 @@ extern bool listExtensions(const char *pattern);
 /* \dx+ */
 extern bool listExtensionContents(const char *pattern);
 
+/* \dX */
+extern bool listExtendedStats(const char *pattern);
+
 /* \dy */
 extern bool listEventTriggers(const char *pattern, bool verbose);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 9ec1c4e810..4883ebd2ed 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -267,6 +267,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
+	fprintf(output, _("  \\dX     [PATTERN]      list extended statistics\n"));
 	fprintf(output, _("  \\dy     [PATTERN]      list event triggers\n"));
 	fprintf(output, _("  \\l[+]   [PATTERN]      list databases\n"));
 	fprintf(output, _("  \\sf[+]  FUNCNAME       show a function's definition\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6abcbea963..17f7265038 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1505,7 +1505,7 @@ psql_completion(const char *text, int start, int end)
 		"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
 		"\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
 		"\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS",
-		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy",
+		"\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
 		"\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding",
 		"\\endif", "\\errverbose", "\\ev",
 		"\\f",
@@ -3974,6 +3974,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (TailMatchesCS("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+	else if (TailMatchesCS("\\dX*"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
 	else if (TailMatchesCS("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (TailMatchesCS("\\dE*"))
-- 
2.16.5

#75Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#74)
Re: list of extended statistics on psql

On 1/20/21 7:41 AM, Tatsuro Yamada wrote:

Hi Tomas,

On 2021/01/20 11:35, Tatsuro Yamada wrote:

Apologies for all the extra work - I haven't realized this flaw when
pushing for showing more stuff :-(

Don't worry about it. We didn't notice the problem even when viewed by
multiple
people on -hackers. Let's keep moving forward. :-D

I'll send a patch including a regression test on the next patch.

I created patches and my test results on PG10, 11, 12, and 14 are fine.

  0001:
    - Fix query to use pg_statistic_ext only
    - Replace statuses "required" and "built" with "defined"
    - Remove the size columns
    - Fix document
    - Add schema name as a filter condition on the query

  0002:
    - Fix all results of \dX
    - Add new testcase by non-superuser

Please find attached files. :-D

Thanks, I've pushed this. I had to tweak the regression tests a bit, for
two reasons:

1) to change user in regression tests, don't use \connect, but SET ROLE
and RESET ROLE

2) roles in regression tests should use names with regress_ prefix

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#76Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#75)
Re: list of extended statistics on psql

Hi Tomas and hackers,

On 2021/01/21 7:00, Tomas Vondra wrote:

I created patches and my test results on PG10, 11, 12, and 14 are fine.

   0001:
     - Fix query to use pg_statistic_ext only
     - Replace statuses "required" and "built" with "defined"
     - Remove the size columns
     - Fix document
     - Add schema name as a filter condition on the query

   0002:
     - Fix all results of \dX
     - Add new testcase by non-superuser

Please find attached files. :-D

Thanks, I've pushed this. I had to tweak the regression tests a bit, for two reasons:

1) to change user in regression tests, don't use \connect, but SET ROLE and RESET ROLE

2) roles in regression tests should use names with regress_ prefix

Thanks for reviewing many times and committing the feature!

I understood 1) and 2). I'll keep that in mind for the next developing patch.
Then, If possible, could you add Justin to the commit message as a reviewer?
Because I revised the document partly based on his comments.

Finally, As extended stats were more used, this feature becomes more useful.
I hope it helps DBA. :-D

Thanks,
Tatsuro Yamada

#77Justin Pryzby
pryzby@telsasoft.com
In reply to: Tomas Vondra (#75)
Re: list of extended statistics on psql (\dX)

On Wed, Jan 20, 2021 at 11:00:50PM +0100, Tomas Vondra wrote:

Thanks, I've pushed this. I had to tweak the regression tests a bit, for two
reasons:

\dX isn't checking schema visibility rules, so accidentally shows stats objects
outside of the search path. I noticed after installing the PG14b1 client,
since we create stats objects in a separate schema to allow excluding them with
pg_dump -N.

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 195f8d8cd2..e29f13c65e 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4774,7 +4774,7 @@ listExtendedStats(const char *pattern)
 	processSQLNamePattern(pset.db, &buf, pattern,
 						  false, false,
 						  "es.stxnamespace::pg_catalog.regnamespace::text", "es.stxname",
-						  NULL, NULL);
+						  NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)");

appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");

#78Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Justin Pryzby (#77)
Re: list of extended statistics on psql (\dX)

On 5/30/21 7:24 PM, Justin Pryzby wrote:

On Wed, Jan 20, 2021 at 11:00:50PM +0100, Tomas Vondra wrote:

Thanks, I've pushed this. I had to tweak the regression tests a bit, for two
reasons:

\dX isn't checking schema visibility rules, so accidentally shows stats objects
outside of the search path. I noticed after installing the PG14b1 client,
since we create stats objects in a separate schema to allow excluding them with
pg_dump -N.

diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 195f8d8cd2..e29f13c65e 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4774,7 +4774,7 @@ listExtendedStats(const char *pattern)
processSQLNamePattern(pset.db, &buf, pattern,
false, false,
"es.stxnamespace::pg_catalog.regnamespace::text", "es.stxname",
-						  NULL, NULL);
+						  NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)");

appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");

Thanks for noticing this! Will push.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#79Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tomas Vondra (#78)
1 attachment(s)
Re: list of extended statistics on psql (\dX)

Hi,

Here's a slightly more complete patch, tweaking the regression tests a
bit to detect this.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

list-stats-schema-fix.patchtext/x-patch; charset=UTF-8; name=list-stats-schema-fix.patchDownload
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2abf255798..eba659705e 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -4774,7 +4774,7 @@ listExtendedStats(const char *pattern)
 	processSQLNamePattern(pset.db, &buf, pattern,
 						  false, false,
 						  "es.stxnamespace::pg_catalog.regnamespace::text", "es.stxname",
-						  NULL, NULL);
+						  NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)");
 
 	appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
 
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 8c214d8dfc..fa9fa9c8f0 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -2987,6 +2987,7 @@ create statistics stts_s1.stts_foo on col1, col2 from stts_t3;
 create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_t3;
 insert into stts_t1 select i,i from generate_series(1,100) i;
 analyze stts_t1;
+set search_path to public, stts_s1, stts_s2, tststats;
 \dX
                                                            List of extended statistics
   Schema  |          Name          |                               Definition                               | Ndistinct | Dependencies |   MCV   
@@ -3002,7 +3003,7 @@ analyze stts_t1;
  public   | stts_hoge              | col1, col2, col3 FROM stts_t3                                          | defined   | defined      | defined
  stts_s1  | stts_foo               | col1, col2 FROM stts_t3                                                | defined   | defined      | defined
  stts_s2  | stts_yama              | col1, col3 FROM stts_t3                                                |           | defined      | defined
- tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl                                       |           |              | defined
+ tststats | priv_test_stats        | a, b FROM priv_test_tbl                                                |           |              | defined
 (12 rows)
 
 \dX stts_?
@@ -3037,7 +3038,7 @@ analyze stts_t1;
  public   | stts_hoge              | col1, col2, col3 FROM stts_t3                                          | defined   | defined      | defined
  stts_s1  | stts_foo               | col1, col2 FROM stts_t3                                                | defined   | defined      | defined
  stts_s2  | stts_yama              | col1, col3 FROM stts_t3                                                |           | defined      | defined
- tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl                                       |           |              | defined
+ tststats | priv_test_stats        | a, b FROM priv_test_tbl                                                |           |              | defined
 (12 rows)
 
 \dX+ stts_?
@@ -3064,30 +3065,45 @@ analyze stts_t1;
  stts_s2 | stts_yama | col1, col3 FROM stts_t3 |           | defined      | defined
 (1 row)
 
+set search_path to public, stts_s1;
+\dX
+                                                          List of extended statistics
+ Schema  |          Name          |                               Definition                               | Ndistinct | Dependencies |   MCV   
+---------+------------------------+------------------------------------------------------------------------+-----------+--------------+---------
+ public  | func_deps_stat         | ((a * 2)), upper(b), ((c + (1)::numeric)) FROM functional_dependencies |           | defined      | 
+ public  | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays                                          |           |              | defined
+ public  | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool                                            |           |              | defined
+ public  | mcv_lists_stats        | a, b, d FROM mcv_lists                                                 |           |              | defined
+ public  | stts_1                 | a, b FROM stts_t1                                                      | defined   |              | 
+ public  | stts_2                 | a, b FROM stts_t1                                                      | defined   | defined      | 
+ public  | stts_3                 | a, b FROM stts_t1                                                      | defined   | defined      | defined
+ public  | stts_4                 | b, c FROM stts_t2                                                      | defined   | defined      | defined
+ public  | stts_hoge              | col1, col2, col3 FROM stts_t3                                          | defined   | defined      | defined
+ stts_s1 | stts_foo               | col1, col2 FROM stts_t3                                                | defined   | defined      | defined
+(10 rows)
+
 create role regress_stats_ext nosuperuser;
 set role regress_stats_ext;
 \dX
-                                                           List of extended statistics
-  Schema  |          Name          |                               Definition                               | Ndistinct | Dependencies |   MCV   
-----------+------------------------+------------------------------------------------------------------------+-----------+--------------+---------
- public   | func_deps_stat         | ((a * 2)), upper(b), ((c + (1)::numeric)) FROM functional_dependencies |           | defined      | 
- public   | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays                                          |           |              | defined
- public   | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool                                            |           |              | defined
- public   | mcv_lists_stats        | a, b, d FROM mcv_lists                                                 |           |              | defined
- public   | stts_1                 | a, b FROM stts_t1                                                      | defined   |              | 
- public   | stts_2                 | a, b FROM stts_t1                                                      | defined   | defined      | 
- public   | stts_3                 | a, b FROM stts_t1                                                      | defined   | defined      | defined
- public   | stts_4                 | b, c FROM stts_t2                                                      | defined   | defined      | defined
- public   | stts_hoge              | col1, col2, col3 FROM stts_t3                                          | defined   | defined      | defined
- stts_s1  | stts_foo               | col1, col2 FROM stts_t3                                                | defined   | defined      | defined
- stts_s2  | stts_yama              | col1, col3 FROM stts_t3                                                |           | defined      | defined
- tststats | priv_test_stats        | a, b FROM tststats.priv_test_tbl                                       |           |              | defined
-(12 rows)
+                                                          List of extended statistics
+ Schema |          Name          |                               Definition                               | Ndistinct | Dependencies |   MCV   
+--------+------------------------+------------------------------------------------------------------------+-----------+--------------+---------
+ public | func_deps_stat         | ((a * 2)), upper(b), ((c + (1)::numeric)) FROM functional_dependencies |           | defined      | 
+ public | mcv_lists_arrays_stats | a, b, c FROM mcv_lists_arrays                                          |           |              | defined
+ public | mcv_lists_bool_stats   | a, b, c FROM mcv_lists_bool                                            |           |              | defined
+ public | mcv_lists_stats        | a, b, d FROM mcv_lists                                                 |           |              | defined
+ public | stts_1                 | a, b FROM stts_t1                                                      | defined   |              | 
+ public | stts_2                 | a, b FROM stts_t1                                                      | defined   | defined      | 
+ public | stts_3                 | a, b FROM stts_t1                                                      | defined   | defined      | defined
+ public | stts_4                 | b, c FROM stts_t2                                                      | defined   | defined      | defined
+ public | stts_hoge              | col1, col2, col3 FROM stts_t3                                          | defined   | defined      | defined
+(9 rows)
 
 reset role;
 drop table stts_t1, stts_t2, stts_t3;
 drop schema stts_s1, stts_s2 cascade;
 drop user regress_stats_ext;
+reset search_path;
 -- User with no access
 CREATE USER regress_stats_user1;
 GRANT USAGE ON SCHEMA tststats TO regress_stats_user1;
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index e033080d4f..d563c4654c 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -1529,6 +1529,7 @@ create statistics stts_s2.stts_yama (dependencies, mcv) on col1, col3 from stts_
 
 insert into stts_t1 select i,i from generate_series(1,100) i;
 analyze stts_t1;
+set search_path to public, stts_s1, stts_s2, tststats;
 
 \dX
 \dX stts_?
@@ -1538,6 +1539,9 @@ analyze stts_t1;
 \dX+ *stts_hoge
 \dX+ stts_s2.stts_yama
 
+set search_path to public, stts_s1;
+\dX
+
 create role regress_stats_ext nosuperuser;
 set role regress_stats_ext;
 \dX
@@ -1546,6 +1550,7 @@ reset role;
 drop table stts_t1, stts_t2, stts_t3;
 drop schema stts_s1, stts_s2 cascade;
 drop user regress_stats_ext;
+reset search_path;
 
 -- User with no access
 CREATE USER regress_stats_user1;
#80Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#79)
Re: list of extended statistics on psql (\dX)

Hi Tomas and Justin,

On 2021/06/07 4:47, Tomas Vondra wrote:

Here's a slightly more complete patch, tweaking the regression tests a
bit to detect this.

I tested your patch on PG14beta2 and PG15devel.
And they work fine.
=======================
All 209 tests passed.
=======================

Next time I create a feature on psql, I will be careful to add
a check for schema visibility rules. :-D

Thanks,
Tatsuro Yamada

#81Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tatsuro Yamada (#80)
Re: list of extended statistics on psql (\dX)

Hi,

I've pushed the last version of the fix, including the regression tests
etc. Backpatch to 14, where \dX was introduced.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#82Tatsuro Yamada
tatsuro.yamada.tf@nttcom.co.jp
In reply to: Tomas Vondra (#81)
Re: list of extended statistics on psql (\dX)

Hi Tomas and Justin,

On 2021/07/27 4:26, Tomas Vondra wrote:

Hi,

I've pushed the last version of the fix, including the regression tests etc. Backpatch to 14, where \dX was introduced.

Thank you!

Regards,
Tatsuro Yamada