diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 20ba105160..67ecec27cf 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1231,6 +1231,97 @@ testdb=>
+
+
+ \dAc[+]
+ [access-method-pattern
+ [input-type-pattern]]
+
+
+
+
+ Lists operator classes
+ (see ).
+ If access-method-patttern
+ is specified, only operator classes associated with access methods whose
+ names match pattern are listed.
+ If input-type-pattern
+ is specified, only operator classes associated with input types whose
+ names match the pattern are listed.
+ If + is appended to the command name, each operator
+ class is listed with its associated operator family and owner.
+
+
+
+
+
+
+ \dAf[+]
+ [access-method-pattern
+ [input-type-pattern]]
+
+
+
+
+ Lists operator families
+ (see ).
+ If access-method-patttern
+ is specified, only operator families associated with access methods whose
+ names match pattern are listed.
+ If input-type-pattern
+ is specified, only operator families associated with input types whose
+ names match the pattern are listed.
+ If + is appended to the command name, each operator
+ family is listed with its owner.
+
+
+
+
+
+
+ \dAo[+]
+ [access-method-pattern
+ [operator-family-pattern]]
+
+
+
+
+
+ Lists operators associated with operator families
+ ().
+ If access-method-patttern
+ is specified, only members of operator families associated with access
+ methods whose names match pattern are listed.
+ If input-type-pattern
+ is specified, only memeber of operator families whose names match the
+ pattern are listed.
+ If + is appended to the command name, each operator
+ is listed with its strategy number, purpose and sort operator family.
+
+
+
+
+
+
+ \dAp[+]
+ [access-method-pattern
+ [operator-family-pattern]]
+
+
+
+
+ Lists procedures associated with operator families
+ ().
+ If access-method-patttern
+ is specified, only members of operator families associated with access
+ methods whose names match pattern are listed.
+ If input-type-pattern
+ is specified, only memeber of operator families whose names match the
+ pattern are listed.
+
+
+
+
\db[+] [ pattern ]
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index e111cee556..abb18a19c2 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -721,7 +721,38 @@ 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 && cmd[2] != '\0' && cmd[2] != '+')
+ pattern2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true);
+
+ switch (cmd[2])
+ {
+ case '\0':
+ case '+':
+ success = describeAccessMethods(pattern, show_verbose);
+ break;
+ case 'c':
+ success = listOperatorClasses(pattern, pattern2, show_verbose);
+ break;
+ case 'f':
+ success = listOperatorFamilies(pattern, pattern2, show_verbose);
+ break;
+ case 'o':
+ success = listOpFamilyOperators(pattern, pattern2, show_verbose);
+ break;
+ case 'p':
+ success = listOpFamilyProcedures(pattern, pattern2);
+ 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 f3c7eb96fa..109245fea7 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"
@@ -6015,3 +6016,337 @@ printACLColumn(PQExpBuffer buf, const char *colname)
"pg_catalog.array_to_string(%s, '\\n') AS \"%s\"",
colname, gettext_noop("Access privileges"));
}
+
+/*
+ * \dAc
+ * Lists operator classes
+ *
+ * Takes an optional regexps to filter by index access method and type.
+ */
+bool
+listOperatorClasses(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 = _("List of 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;
+}
+
+/*
+ * \dAf
+ * Lists operator families
+ *
+ * Takes an optional regexps to filter by index access method and type.
+ */
+bool
+listOperatorFamilies(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"
+ " CASE\n"
+ " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
+ " THEN format('%%I', f.opfname)\n"
+ " ELSE format('%%I.%%I', n.nspname, f.opfname)\n"
+ " END AS \"%s\",\n"
+ " (SELECT\n"
+ " string_agg(format_type(oc.opcintype, -1), ', ')\n"
+ " FROM pg_opclass oc\n"
+ " WHERE oc.opcfamily = f.oid) \"%s\"",
+ gettext_noop("AM"),
+ gettext_noop("Operator family"),
+ gettext_noop("Applicable types"));
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
+ gettext_noop("Owner"));
+ appendPQExpBuffer(&buf,
+ "\nFROM pg_catalog.pg_opfamily f\n"
+ " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
+ " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.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)
+ {
+ appendPQExpBuffer(&buf,
+ "\n %s EXISTS (\n"
+ " SELECT 1\n"
+ " FROM pg_type t\n"
+ " JOIN pg_opclass oc ON oc.opcintype = t.oid\n"
+ " WHERE oc.opcfamily = f.oid",
+ have_where ? "AND" : "WHERE");
+ processSQLNamePattern(pset.db, &buf, type_pattern, true, false,
+ NULL, "t.typname", NULL, NULL);
+ appendPQExpBuffer(&buf, ")");
+ }
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of operator families");
+ 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;
+}
+
+/*
+ * \dAo
+ * Lists operators of operator families
+ *
+ * Takes an optional regexps to filter by index access method and operator
+ * family.
+ */
+bool
+listOpFamilyOperators(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 of operators of operator families");
+ 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 of operator families
+ *
+ * Takes an optional regexps to filter by index access method and operator
+ * family.
+ */
+bool
+listOpFamilyProcedures(const char *access_method_pattern,
+ const char *family_pattern)
+{
+ 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,"
+ " p.proname AS \"%s\"\n",
+ gettext_noop("AM"),
+ gettext_noop("Operator family"),
+ gettext_noop("Left arg type"),
+ gettext_noop("Right arg type"),
+ gettext_noop("Number"),
+ 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"
+ " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.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 procedures of operator families");
+ 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 20dbfd20f0..35c50e3bcd 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -114,4 +114,23 @@ bool describePublications(const char *pattern);
/* \dRs */
bool describeSubscriptions(const char *pattern, bool verbose);
+/* \dAc */
+extern bool listOperatorClasses(const char *access_method_pattern,
+ const char *opclass_pattern,
+ bool verbose);
+
+/* \dAf */
+extern bool listOperatorFamilies(const char *access_method_pattern,
+ const char *opclass_pattern,
+ bool verbose);
+
+/* \dAo */
+extern bool listOpFamilyOperators(const char *accessMethod_pattern,
+ const char *family_pattern, bool verbose);
+
+/* \dAp */
+extern bool listOpFamilyProcedures(const char *access_method_pattern,
+ const char *family_pattern);
+
+
#endif /* DESCRIBE_H */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 1f1f778426..9a18cb3059 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,6 +227,10 @@ 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\n"));
+ fprintf(output, _(" \\dAf[+] [AMPTRN [TYPEPTRN]] list operator families\n"));
+ fprintf(output, _(" \\dAo[+] [AMPTRN [OPFPTRN]] list operators of operator families\n"));
+ fprintf(output, _(" \\dAp [AMPTRN [OPFPTRN]] list procedures of operator families\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 17b1f299b4..174c3db623 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -510,6 +510,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",
@@ -1462,7 +1469,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", "\\dAc", "\\dAf", "\\dAo", "\\dAp",
+ "\\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",
@@ -3698,6 +3706,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("\\dAc*", MatchAny) ||
+ TailMatchesCS("\\dAf*", MatchAny))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+ else if (TailMatchesCS("\\dAo*", MatchAny) ||
+ TailMatchesCS("\\dAp*", MatchAny))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families, 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 242f817163..2423ae2f37 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4809,3 +4809,165 @@ 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 foo bar
+List of access methods
+ Name | Type
+------+------
+(0 rows)
+
+\dA: extra argument "bar" ignored
+\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)
+
+\dAc brin pg*.oid*
+ List of operator classes
+ AM | Input type | Storage type | Operator class | Default?
+------+------------+--------------+----------------+----------
+ brin | oid | | oid_minmax_ops | yes
+(1 row)
+
+\dAf spgist
+ List of operator families
+ AM | Operator family | Applicable types
+--------+-----------------+------------------
+ spgist | box_ops | box
+ spgist | kd_point_ops | point
+ spgist | network_ops | inet
+ spgist | poly_ops | polygon
+ spgist | quad_point_ops | point
+ spgist | range_ops | anyrange
+ spgist | text_ops | text
+(7 rows)
+
+\dAf btree int4
+ List of operator families
+ AM | Operator family | Applicable types
+-------+-----------------+---------------------------
+ btree | integer_ops | smallint, integer, bigint
+(1 row)
+
+\dAo brin uuid_minmax_ops
+ List of operators of operator families
+ 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 of operators of operator families
+ 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 procedures of operator families
+ AM | Operator family | Left arg type | Right arg type | Number | Proc name
+------+-----------------+---------------+----------------+--------+------------------------
+ brin | uuid_minmax_ops | uuid | uuid | 1 | brin_minmax_opcinfo
+ brin | uuid_minmax_ops | uuid | uuid | 2 | brin_minmax_add_value
+ brin | uuid_minmax_ops | uuid | uuid | 3 | brin_minmax_consistent
+ brin | uuid_minmax_ops | uuid | uuid | 4 | brin_minmax_union
+(4 rows)
+
+\dAp * pg_catalog.uuid_ops
+ List of procedures of operator families
+ AM | Operator family | Left arg type | Right arg type | Number | Proc name
+-------+-----------------+---------------+----------------+--------+--------------------
+ btree | uuid_ops | uuid | uuid | 1 | uuid_cmp
+ btree | uuid_ops | uuid | uuid | 2 | uuid_sortsupport
+ btree | uuid_ops | uuid | uuid | 4 | btequalimage
+ hash | uuid_ops | uuid | uuid | 1 | uuid_hash
+ hash | uuid_ops | uuid | uuid | 2 | uuid_hash_extended
+(5 rows)
+
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 26a0bcf718..3c876d2699 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1182,3 +1182,21 @@ 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 foo bar
+\dA+
+\dA+ *
+\dA+ h*
+\dA+ foo
+\dAc brin pg*.oid*
+\dAf spgist
+\dAf btree int4
+\dAo brin uuid_minmax_ops
+\dAo * pg_catalog.jsonb_path_ops
+\dAp brin uuid_minmax_ops
+\dAp * pg_catalog.uuid_ops