commit 0e0d0c9b24b84f15640bdb3ab60ad9696ed70a8f Author: Alexander Korotkov Date: Thu Aug 1 01:15:08 2019 +0300 Initial. Reported-by: Bug: Discussion: Author: Reviewed-by: Tested-by: Backpatch-through: diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 68ad5071cab..ec79c110b70 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -681,7 +681,7 @@ search and ordering purposes.) - +
<structname>pg_amop</structname> Columns @@ -824,7 +824,7 @@ is one row for each support function belonging to an operator family. -
+
<structname>pg_amproc</structname> Columns @@ -4467,7 +4467,7 @@ SCRAM-SHA-256$<iteration count>:&l Operator classes are described at length in . -
+
<structname>pg_opclass</structname> Columns @@ -4729,7 +4729,7 @@ SCRAM-SHA-256$<iteration count>:&l Operator families are described at length in . -
+
<structname>pg_opfamily</structname> Columns diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 7789fc61776..27772ec50f4 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1231,6 +1231,76 @@ testdb=> + + + \dAc[+] + [access-method-pattern + [input-type-pattern]] + + + + + Shows info index access method operator classes listed in + . + If access-method-patttern + is specified, only operator classes associated with access method whose + name matches pattern are shown. + If input-type-pattern + is specified, only procedures associated with families whose input type + matches the pattern are shown. + If + is appended to the command name, operator family + and owner are listed. + + + + + + + \dAo[+] + [access-method-pattern + [operator-family-pattern]] + + + + + + Lists operators () associated + with access method operator families. If + access-method-patttern is + specified, only operators associated with access method whose name + matches pattern are shown. If + operator-family-pattern is + specified, only operators associated with families whose name matches + the pattern are shown. + If + is appended to the command name, displays + additional info. + + + + + + + \dAp[+] + [access-method-pattern + [operator-family-pattern]] + + + + + Lists procedures () associated + with access method operator families. + If access-method-patttern + is specified, only procedures associated with access method whose name + matches pattern are shown. + If operator-family-pattern + is specified, only procedures associated with families whose name + matches the pattern are shown. + If + is appended to the command name, procedures + listed with its names. + + + + \db[+] [ pattern ] diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index c0a7a5566eb..52a0711ac8b 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -722,7 +722,35 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) success = listTables("tvmsE", NULL, show_verbose, show_system); break; case 'A': - success = describeAccessMethods(pattern, show_verbose); + { + char *pattern2 = NULL; + + if (pattern) + pattern2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); + + switch (cmd[2]) + { + case '\0': + case '+': + success = describeAccessMethods(pattern, show_verbose); + break; + case 'o': + success = listFamilyClassOperators(pattern, pattern2, show_verbose); + break; + case 'p': + success = listOperatorFamilyProcedures(pattern, pattern2, show_verbose); + break; + case 'c': + success = describeAccessMethodOperatorClasses(pattern, pattern2, show_verbose); + break; + default: + status = PSQL_CMD_UNKNOWN; + break; + } + + if (pattern2) + free(pattern2); + } break; case 'a': success = describeAggregates(pattern, show_verbose, show_system); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 774cc764ff8..be9f2dd3a09 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -14,6 +14,7 @@ #include +#include "catalog/pg_am.h" #include "catalog/pg_attribute_d.h" #include "catalog/pg_cast_d.h" #include "catalog/pg_class_d.h" @@ -6017,3 +6018,257 @@ printACLColumn(PQExpBuffer buf, const char *colname) "pg_catalog.array_to_string(%s, '\\n') AS \"%s\"", colname, gettext_noop("Access privileges")); } + +/* + * \dAo + * Lists operators associated with access method operator families. + * + * Takes an optional regexp to select particular access methods + * and operator families + */ +bool +listFamilyClassOperators(const char *access_method_pattern, + const char *family_pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + bool have_where = false; + + static const bool translate_columns[] = {false, false, false, false, false, + false, false, true, false}; + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, + "SELECT\n" + " am.amname AS \"%s\",\n" + " CASE\n" + " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" + " THEN format('%%I', of.opfname)\n" + " ELSE format('%%I.%%I', nsf.nspname, of.opfname)\n" + " END AS \"%s\",\n" + " format ('%%s (%%s, %%s)',\n" + " CASE\n" + " WHEN pg_catalog.pg_operator_is_visible(op.oid) \n" + " THEN op.oprname::pg_catalog.text \n" + " ELSE o.amopopr::pg_catalog.regoper::pg_catalog.text \n" + " END,\n" + " pg_catalog.format_type(o.amoplefttype, NULL),\n" + " pg_catalog.format_type(o.amoprighttype, NULL)\n" + " ) AS \"%s\"\n", + gettext_noop("AM"), + gettext_noop("Opfamily Name"), + gettext_noop("Operator")); + + if (verbose) + appendPQExpBuffer(&buf, + ", o.amopstrategy AS \"%s\",\n" + " CASE o.amoppurpose\n" + " WHEN 'o' THEN '%s'\n" + " WHEN 's' THEN '%s'\n" + " END AS \"%s\",\n" + " ofs.opfname AS \"%s\"\n", + gettext_noop("Strategy"), + gettext_noop("ordering"), + gettext_noop("search"), + gettext_noop("Purpose"), + gettext_noop("Sort opfamily")); + appendPQExpBuffer(&buf, + "FROM pg_catalog.pg_amop o\n" + " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n" + " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n" + " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n" + " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n"); + if (verbose) + appendPQExpBuffer(&buf, + " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"); + + if (access_method_pattern) + have_where = processSQLNamePattern(pset.db, &buf, access_method_pattern, + false, false, NULL, "am.amname", + NULL, NULL); + + if (family_pattern) + processSQLNamePattern(pset.db, &buf, family_pattern, have_where, false, + "nsf.nspname", "of.opfname", NULL, NULL); + + appendPQExpBufferStr(&buf, "ORDER BY 1, 2, o.amopstrategy, 3;"); + + res = PSQLexec(buf.data); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("List operators of family related to access method"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); + + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + PQclear(res); + return true; +} + +/* + * \dAp + * Lists procedures associated with access method operator families. + * + * Takes an optional regexp to select particular access methods + * and operator families + */ +bool +listOperatorFamilyProcedures(const char *access_method_pattern, + const char *family_pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + bool have_where = false; + static const bool translate_columns[] = {false, false, false, false, false, false, false}; + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, + "SELECT DISTINCT\n" + " am.amname AS \"%s\",\n" + " CASE\n" + " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" + " THEN format('%%I', of.opfname)\n" + " ELSE format('%%I.%%I', ns.nspname, of.opfname)\n" + " END AS \"%s\",\n" + " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n" + " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n" + " ap.amprocnum AS \"%s\"\n", + gettext_noop("AM"), + gettext_noop("Operator family"), + gettext_noop("Left arg type"), + gettext_noop("Right arg type"), + gettext_noop("Support function")); + if (verbose) + appendPQExpBuffer(&buf, + ", ap.amproc::pg_catalog.regproc::pg_catalog.text AS \"%s\"\n", + gettext_noop("Proc name")); + appendPQExpBuffer(&buf, + "FROM pg_catalog.pg_amproc ap\n" + " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n" + " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n" + " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"); + + if (access_method_pattern) + have_where = processSQLNamePattern(pset.db, &buf, access_method_pattern, + false, false, NULL, "am.amname", + NULL, NULL); + if (family_pattern) + processSQLNamePattern(pset.db, &buf, family_pattern, have_where, false, + "ns.nspname", "of.opfname", NULL, NULL); + + appendPQExpBufferStr(&buf, + "ORDER BY 1, 2, 3, 4, 5;"); + + res = PSQLexec(buf.data); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("List of operator family procedures"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); + + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + PQclear(res); + return true; +} + +/* + * \dAc + * List index access method operator classes. + * Takes an optional regexp to select particular access method and operator class. + */ +bool +describeAccessMethodOperatorClasses(const char *access_method_pattern, + const char *type_pattern, bool verbose) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + bool have_where = false; + static const bool translate_columns[] = {false, false, false, false, false, + false, false, false}; + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, + "SELECT DISTINCT" + " am.amname AS \"%s\",\n" + " c.opcintype::pg_catalog.regtype AS \"%s\",\n" + " (CASE WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n" + " THEN c.opckeytype\n" + " ELSE NULL -- c.opcintype\n" + " END)::pg_catalog.regtype AS \"%s\",\n" + " CASE\n" + " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n" + " THEN format('%%I', c.opcname)\n" + " ELSE format('%%I.%%I', n.nspname, c.opcname)\n" + " END AS \"%s\",\n" + " (CASE WHEN c.opcdefault\n" + " THEN '%s'\n" + " ELSE '%s'\n" + " END) AS \"%s\"", + gettext_noop("AM"), + gettext_noop("Input type"), + gettext_noop("Storage type"), + gettext_noop("Operator class"), + gettext_noop("yes"), + gettext_noop("no"), + gettext_noop("Default?")); + if (verbose) + appendPQExpBuffer(&buf, + ",\n CASE\n" + " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" + " THEN format('%%I', of.opfname)\n" + " ELSE format('%%I.%%I', ofn.nspname, of.opfname)\n" + " END AS \"%s\",\n" + " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n", + gettext_noop("Operator family"), + gettext_noop("Owner")); + appendPQExpBuffer(&buf, + "\nFROM pg_catalog.pg_opclass c\n" + " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n" + " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n" + " LEFT JOIN pg_catalog.pg_type t1 ON t1.oid = c.opcintype\n" + ); + if (verbose) + appendPQExpBuffer(&buf, + " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n" + " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n"); + + if (access_method_pattern) + have_where = processSQLNamePattern(pset.db, &buf, access_method_pattern, + false, false, NULL, "am.amname", NULL, NULL); + if (type_pattern) + processSQLNamePattern(pset.db, &buf, type_pattern, have_where, false, + NULL, "t1.typname", NULL, NULL); + + appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;"); + res = PSQLexec(buf.data); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("Index access method operator classes"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); + + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + PQclear(res); + return true; +} diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 17736c37827..9f31fd528d4 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -114,4 +114,18 @@ bool describePublications(const char *pattern); /* \dRs */ bool describeSubscriptions(const char *pattern, bool verbose); +/* \dAp */ +extern bool listOperatorFamilyProcedures(const char *access_method_pattern, + const char *family_pattern, + bool verbose); + +/* \dAo */ +extern bool listFamilyClassOperators(const char *accessMethod_pattern, + const char *family_pattern, bool verbose); + +/* \dAc */ +extern bool describeAccessMethodOperatorClasses(const char *access_method_pattern, + const char *opclass_pattern, + bool verbose); + #endif /* DESCRIBE_H */ diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index d9b982d3a0b..8e0d9c7d758 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -227,6 +227,9 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n")); fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n")); fprintf(output, _(" \\dA[+] [PATTERN] list access methods\n")); + fprintf(output, _(" \\dAc[+] [AMPTRN [TYPEPTRN]] list operator classes of index access methods\n")); + fprintf(output, _(" \\dAo[+] [AMPTRN [OPFPTRN]] list operators of family related to access method\n")); + fprintf(output, _(" \\dAp[+] [AMPTRN [OPFPTRN]] list procedures of operator family related to access method\n")); fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n")); fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n")); fprintf(output, _(" \\dC[+] [PATTERN] list casts\n")); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 3f7001fb696..5d14a931cbc 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -495,6 +495,13 @@ static const SchemaQuery Query_for_list_of_partitioned_relations = { .result = "pg_catalog.quote_ident(c.relname)", }; +static const SchemaQuery Query_for_list_of_operator_families = { + .catname = "pg_catalog.pg_opfamily c", + .viscondition = "pg_catalog.pg_opfamily_is_visible(c.oid)", + .namespace = "c.opfnamespace", + .result = "pg_catalog.quote_ident(c.opfname)", +}; + /* Relations supporting INSERT, UPDATE or DELETE */ static const SchemaQuery Query_for_list_of_updatables = { .catname = "pg_catalog.pg_class c", @@ -1417,7 +1424,8 @@ psql_completion(const char *text, int start, int end) "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright", "\\crosstabview", - "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD", + "\\d", "\\da", "\\dA", "\\dAp", "\\dAo", "\\dAp", "\\dAc", + "\\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", "\\dP", "\\dPi", "\\dPt", @@ -3590,6 +3598,12 @@ psql_completion(const char *text, int start, int end) } else if (TailMatchesCS("\\da*")) COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); + else if (TailMatchesCS("\\dAp*", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families, NULL); + else if (TailMatchesCS("\\dAo*", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families, NULL); + else if (TailMatchesCS("\\dAc*", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); else if (TailMatchesCS("\\dA*")) COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); else if (TailMatchesCS("\\db*")) diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index ef534a36a06..4a67e2442da 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -4760,3 +4760,137 @@ Owning table: "pg_catalog.pg_statistic" Indexes: "pg_toast_2619_index" PRIMARY KEY, btree (chunk_id, chunk_seq) +-- check printing info about access methods +\dA +List of access methods + Name | Type +--------+------- + brin | Index + btree | Index + gin | Index + gist | Index + hash | Index + heap | Table + heap2 | Table + spgist | Index +(8 rows) + +\dA * +List of access methods + Name | Type +--------+------- + brin | Index + btree | Index + gin | Index + gist | Index + hash | Index + heap | Table + heap2 | Table + spgist | Index +(8 rows) + +\dA h* +List of access methods + Name | Type +-------+------- + hash | Index + heap | Table + heap2 | Table +(3 rows) + +\dA foo +List of access methods + Name | Type +------+------ +(0 rows) + +\dA+ + List of access methods + Name | Type | Handler | Description +--------+-------+----------------------+---------------------------------------- + brin | Index | brinhandler | block range index (BRIN) access method + btree | Index | bthandler | b-tree index access method + gin | Index | ginhandler | GIN index access method + gist | Index | gisthandler | GiST index access method + hash | Index | hashhandler | hash index access method + heap | Table | heap_tableam_handler | heap table access method + heap2 | Table | heap_tableam_handler | + spgist | Index | spghandler | SP-GiST index access method +(8 rows) + +\dA+ * + List of access methods + Name | Type | Handler | Description +--------+-------+----------------------+---------------------------------------- + brin | Index | brinhandler | block range index (BRIN) access method + btree | Index | bthandler | b-tree index access method + gin | Index | ginhandler | GIN index access method + gist | Index | gisthandler | GiST index access method + hash | Index | hashhandler | hash index access method + heap | Table | heap_tableam_handler | heap table access method + heap2 | Table | heap_tableam_handler | + spgist | Index | spghandler | SP-GiST index access method +(8 rows) + +\dA+ h* + List of access methods + Name | Type | Handler | Description +-------+-------+----------------------+-------------------------- + hash | Index | hashhandler | hash index access method + heap | Table | heap_tableam_handler | heap table access method + heap2 | Table | heap_tableam_handler | +(3 rows) + +\dA+ foo + List of access methods + Name | Type | Handler | Description +------+------+---------+------------- +(0 rows) + +\dAo brin uuid_minmax_ops +List operators of family related to access method + AM | Opfamily Name | Operator +------+-----------------+----------------- + brin | uuid_minmax_ops | < (uuid, uuid) + brin | uuid_minmax_ops | <= (uuid, uuid) + brin | uuid_minmax_ops | = (uuid, uuid) + brin | uuid_minmax_ops | >= (uuid, uuid) + brin | uuid_minmax_ops | > (uuid, uuid) +(5 rows) + +\dAo * pg_catalog.jsonb_path_ops +List operators of family related to access method + AM | Opfamily Name | Operator +-----+----------------+---------------------- + gin | jsonb_path_ops | @> (jsonb, jsonb) + gin | jsonb_path_ops | @? (jsonb, jsonpath) + gin | jsonb_path_ops | @@ (jsonb, jsonpath) +(3 rows) + +\dAp brin uuid_minmax_ops + List of operator family procedures + AM | Operator family | Left arg type | Right arg type | Support function +------+-----------------+---------------+----------------+------------------ + brin | uuid_minmax_ops | uuid | uuid | 1 + brin | uuid_minmax_ops | uuid | uuid | 2 + brin | uuid_minmax_ops | uuid | uuid | 3 + brin | uuid_minmax_ops | uuid | uuid | 4 +(4 rows) + +\dAp * pg_catalog.uuid_ops + List of operator family procedures + AM | Operator family | Left arg type | Right arg type | Support function +-------+-----------------+---------------+----------------+------------------ + btree | uuid_ops | uuid | uuid | 1 + btree | uuid_ops | uuid | uuid | 2 + hash | uuid_ops | uuid | uuid | 1 + hash | uuid_ops | uuid | uuid | 2 +(4 rows) + +\dAc brin pg*.oid* + Index access method operator classes + AM | Input type | Storage type | Operator class | Default? +------+------------+--------------+----------------+---------- + brin | oid | | oid_minmax_ops | yes +(1 row) + diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index 2e379849625..9f9dfda3994 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -1134,3 +1134,18 @@ drop role regress_partitioning_role; -- \d on toast table (use pg_statistic's toast table, which has a known name) \d pg_toast.pg_toast_2619 + +-- check printing info about access methods +\dA +\dA * +\dA h* +\dA foo +\dA+ +\dA+ * +\dA+ h* +\dA+ foo +\dAo brin uuid_minmax_ops +\dAo * pg_catalog.jsonb_path_ops +\dAp brin uuid_minmax_ops +\dAp * pg_catalog.uuid_ops +\dAc brin pg*.oid*