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