aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/pg_amcheck/pg_amcheck.c27
-rw-r--r--src/bin/pg_amcheck/t/002_nonesuch.pl99
-rw-r--r--src/bin/pg_dump/pg_dump.c65
-rw-r--r--src/bin/pg_dump/pg_dumpall.c13
-rw-r--r--src/bin/pg_dump/t/002_pg_dump.pl107
-rw-r--r--src/bin/psql/describe.c504
-rw-r--r--src/fe_utils/string_utils.c129
-rw-r--r--src/include/fe_utils/string_utils.h6
-rw-r--r--src/test/regress/expected/psql.out804
-rw-r--r--src/test/regress/sql/psql.sql242
10 files changed, 1781 insertions, 215 deletions
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 90471e096d2..48cee8c1c4e 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -1308,10 +1308,17 @@ static void
append_database_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
{
PQExpBufferData buf;
+ int dotcnt;
PatternInfo *info = extend_pattern_info_array(pia);
initPQExpBuffer(&buf);
- patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false);
+ patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false, false,
+ &dotcnt);
+ if (dotcnt > 0)
+ {
+ pg_log_error("improper qualified name (too many dotted names): %s", pattern);
+ exit(2);
+ }
info->pattern = pattern;
info->db_regex = pstrdup(buf.data);
@@ -1332,12 +1339,19 @@ append_schema_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
{
PQExpBufferData dbbuf;
PQExpBufferData nspbuf;
+ int dotcnt;
PatternInfo *info = extend_pattern_info_array(pia);
initPQExpBuffer(&dbbuf);
initPQExpBuffer(&nspbuf);
- patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false);
+ patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false, false,
+ &dotcnt);
+ if (dotcnt > 1)
+ {
+ pg_log_error("improper qualified name (too many dotted names): %s", pattern);
+ exit(2);
+ }
info->pattern = pattern;
if (dbbuf.data[0])
{
@@ -1369,13 +1383,20 @@ append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern,
PQExpBufferData dbbuf;
PQExpBufferData nspbuf;
PQExpBufferData relbuf;
+ int dotcnt;
PatternInfo *info = extend_pattern_info_array(pia);
initPQExpBuffer(&dbbuf);
initPQExpBuffer(&nspbuf);
initPQExpBuffer(&relbuf);
- patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false);
+ patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false,
+ false, &dotcnt);
+ if (dotcnt > 2)
+ {
+ pg_log_error("improper relation name (too many dotted names): %s", pattern);
+ exit(2);
+ }
info->pattern = pattern;
if (dbbuf.data[0])
{
diff --git a/src/bin/pg_amcheck/t/002_nonesuch.pl b/src/bin/pg_amcheck/t/002_nonesuch.pl
index 56d55199f84..6c0f97027dd 100644
--- a/src/bin/pg_amcheck/t/002_nonesuch.pl
+++ b/src/bin/pg_amcheck/t/002_nonesuch.pl
@@ -147,6 +147,100 @@ $node->command_checks_all(
[qr/pg_amcheck: error: no heap tables to check matching "\."/],
'checking table pattern "."');
+# Check that a multipart database name is rejected
+$node->command_checks_all(
+ [ 'pg_amcheck', '-d', 'localhost.postgres' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): localhost\.postgres/
+ ],
+ 'multipart database patterns are rejected'
+);
+
+# Check that a three-part schema name is rejected
+$node->command_checks_all(
+ [ 'pg_amcheck', '-s', 'localhost.postgres.pg_catalog' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): localhost\.postgres\.pg_catalog/
+ ],
+ 'three part schema patterns are rejected'
+);
+
+# Check that a four-part table name is rejected
+$node->command_checks_all(
+ [ 'pg_amcheck', '-t', 'localhost.postgres.pg_catalog.pg_class' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper relation name \(too many dotted names\): localhost\.postgres\.pg_catalog\.pg_class/
+ ],
+ 'four part table patterns are rejected'
+);
+
+# Check that too many dotted names still draws an error under --no-strict-names
+# That flag means that it is ok for the object to be missing, not that it is ok
+# for the object name to be ungrammatical
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-t', 'this.is.a.really.long.dotted.string' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper relation name \(too many dotted names\): this\.is\.a\.really\.long\.dotted\.string/
+ ],
+ 'ungrammatical table names still draw errors under --no-strict-names'
+);
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-s', 'postgres.long.dotted.string' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): postgres\.long\.dotted\.string/
+ ],
+ 'ungrammatical schema names still draw errors under --no-strict-names'
+);
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-d', 'postgres.long.dotted.string' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): postgres\.long\.dotted\.string/
+ ],
+ 'ungrammatical database names still draw errors under --no-strict-names'
+);
+
+# Likewise for exclusion patterns
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-T', 'a.b.c.d' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper relation name \(too many dotted names\): a\.b\.c\.d/
+ ],
+ 'ungrammatical table exclusions still draw errors under --no-strict-names'
+);
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-S', 'a.b.c' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): a\.b\.c/
+ ],
+ 'ungrammatical schema exclusions still draw errors under --no-strict-names'
+);
+$node->command_checks_all(
+ [ 'pg_amcheck', '--no-strict-names', '-D', 'a.b' ],
+ 2,
+ [qr/^$/],
+ [
+ qr/pg_amcheck: error: improper qualified name \(too many dotted names\): a\.b/
+ ],
+ 'ungrammatical database exclusions still draw errors under --no-strict-names'
+);
+
+
#########################################
# Test checking non-existent databases, schemas, tables, and indexes
@@ -165,9 +259,7 @@ $node->command_checks_all(
'-d', 'no*such*database',
'-r', 'none.none',
'-r', 'none.none.none',
- '-r', 'this.is.a.really.long.dotted.string',
'-r', 'postgres.none.none',
- '-r', 'postgres.long.dotted.string',
'-r', 'postgres.pg_catalog.none',
'-r', 'postgres.none.pg_class',
'-t', 'postgres.pg_catalog.pg_class', # This exists
@@ -186,15 +278,12 @@ $node->command_checks_all(
qr/pg_amcheck: warning: no connectable databases to check matching "no\*such\*database"/,
qr/pg_amcheck: warning: no relations to check matching "none\.none"/,
qr/pg_amcheck: warning: no connectable databases to check matching "none\.none\.none"/,
- qr/pg_amcheck: warning: no connectable databases to check matching "this\.is\.a\.really\.long\.dotted\.string"/,
qr/pg_amcheck: warning: no relations to check matching "postgres\.none\.none"/,
- qr/pg_amcheck: warning: no relations to check matching "postgres\.long\.dotted\.string"/,
qr/pg_amcheck: warning: no relations to check matching "postgres\.pg_catalog\.none"/,
qr/pg_amcheck: warning: no relations to check matching "postgres\.none\.pg_class"/,
qr/pg_amcheck: warning: no connectable databases to check matching "no_such_database"/,
qr/pg_amcheck: warning: no connectable databases to check matching "no\*such\*database"/,
qr/pg_amcheck: warning: no connectable databases to check matching "none\.none\.none"/,
- qr/pg_amcheck: warning: no connectable databases to check matching "this\.is\.a\.really\.long\.dotted\.string"/,
],
'many unmatched patterns and one matched pattern under --no-strict-names'
);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 969e2a7a462..d3588607e74 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -178,6 +178,9 @@ static void expand_table_name_patterns(Archive *fout,
SimpleStringList *patterns,
SimpleOidList *oids,
bool strict_names);
+static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
+ const char *pattern);
+
static NamespaceInfo *findNamespace(Oid nsoid);
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
@@ -1315,10 +1318,21 @@ expand_schema_name_patterns(Archive *fout,
for (cell = patterns->head; cell; cell = cell->next)
{
+ PQExpBufferData dbbuf;
+ int dotcnt;
+
appendPQExpBufferStr(query,
"SELECT oid FROM pg_catalog.pg_namespace n\n");
+ initPQExpBuffer(&dbbuf);
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
- false, NULL, "n.nspname", NULL, NULL);
+ false, NULL, "n.nspname", NULL, NULL, &dbbuf,
+ &dotcnt);
+ if (dotcnt > 1)
+ pg_fatal("improper qualified name (too many dotted names): %s",
+ cell->val);
+ else if (dotcnt == 1)
+ prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
+ termPQExpBuffer(&dbbuf);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
@@ -1362,10 +1376,16 @@ expand_extension_name_patterns(Archive *fout,
*/
for (cell = patterns->head; cell; cell = cell->next)
{
+ int dotcnt;
+
appendPQExpBufferStr(query,
"SELECT oid FROM pg_catalog.pg_extension e\n");
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
- false, NULL, "e.extname", NULL, NULL);
+ false, NULL, "e.extname", NULL, NULL, NULL,
+ &dotcnt);
+ if (dotcnt > 0)
+ pg_fatal("improper qualified name (too many dotted names): %s",
+ cell->val);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
@@ -1409,10 +1429,16 @@ expand_foreign_server_name_patterns(Archive *fout,
for (cell = patterns->head; cell; cell = cell->next)
{
+ int dotcnt;
+
appendPQExpBufferStr(query,
"SELECT oid FROM pg_catalog.pg_foreign_server s\n");
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
- false, NULL, "s.srvname", NULL, NULL);
+ false, NULL, "s.srvname", NULL, NULL, NULL,
+ &dotcnt);
+ if (dotcnt > 0)
+ pg_fatal("improper qualified name (too many dotted names): %s",
+ cell->val);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) == 0)
@@ -1455,6 +1481,9 @@ expand_table_name_patterns(Archive *fout,
for (cell = patterns->head; cell; cell = cell->next)
{
+ PQExpBufferData dbbuf;
+ int dotcnt;
+
/*
* Query must remain ABSOLUTELY devoid of unqualified names. This
* would be unnecessary given a pg_table_is_visible() variant taking a
@@ -1470,9 +1499,17 @@ expand_table_name_patterns(Archive *fout,
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
RELKIND_PARTITIONED_TABLE);
+ initPQExpBuffer(&dbbuf);
processSQLNamePattern(GetConnection(fout), query, cell->val, true,
false, "n.nspname", "c.relname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
+ &dotcnt);
+ if (dotcnt > 2)
+ pg_fatal("improper relation name (too many dotted names): %s",
+ cell->val);
+ else if (dotcnt == 2)
+ prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
+ termPQExpBuffer(&dbbuf);
ExecuteSqlStatement(fout, "RESET search_path");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -1494,6 +1531,26 @@ expand_table_name_patterns(Archive *fout,
}
/*
+ * Verifies that the connected database name matches the given database name,
+ * and if not, dies with an error about the given pattern.
+ *
+ * The 'dbname' argument should be a literal name parsed from 'pattern'.
+ */
+static void
+prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
+{
+ const char *db;
+
+ db = PQdb(conn);
+ if (db == NULL)
+ pg_fatal("You are currently not connected to a database.");
+
+ if (strcmp(db, dbname) != 0)
+ pg_fatal("cross-database references are not implemented: %s",
+ pattern);
+}
+
+/*
* checkExtensionMembership
* Determine whether object is an extension member, and if so,
* record an appropriate dependency and set the object's dump flag.
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 79a723885ed..52f9f7c4d66 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -1269,10 +1269,21 @@ expand_dbname_patterns(PGconn *conn,
for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next)
{
+ int dotcnt;
+
appendPQExpBufferStr(query,
"SELECT datname FROM pg_catalog.pg_database n\n");
processSQLNamePattern(conn, query, cell->val, false,
- false, NULL, "datname", NULL, NULL);
+ false, NULL, "datname", NULL, NULL, NULL,
+ &dotcnt);
+
+ if (dotcnt > 0)
+ {
+ pg_log_error("improper qualified name (too many dotted names): %s",
+ cell->val);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
res = executeQuery(conn, query->data);
for (int i = 0; i < PQntuples(res); i++)
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index c65c92bfb0e..1ecfd7ae235 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -3974,6 +3974,113 @@ command_fails_like(
'no matching tables');
#########################################
+# Test invalid multipart database names
+
+$node->command_fails_like(
+ [ 'pg_dumpall', '--exclude-database', '.' ],
+ qr/pg_dumpall: error: improper qualified name \(too many dotted names\): \./,
+ 'pg_dumpall: option --exclude-database rejects multipart pattern "."'
+);
+
+$node->command_fails_like(
+ [ 'pg_dumpall', '--exclude-database', '.*' ],
+ qr/pg_dumpall: error: improper qualified name \(too many dotted names\): \.\*/,
+ 'pg_dumpall: option --exclude-database rejects multipart pattern ".*"'
+);
+
+$node->command_fails_like(
+ [ 'pg_dumpall', '--exclude-database', '*.*' ],
+ qr/pg_dumpall: error: improper qualified name \(too many dotted names\): \*\.\*/,
+ 'pg_dumpall: option --exclude-database rejects multipart pattern "*.*"'
+);
+
+$node->command_fails_like(
+ [ 'pg_dumpall', '--exclude-database', 'myhost.mydb' ],
+ qr/pg_dumpall: error: improper qualified name \(too many dotted names\): myhost\.mydb/,
+ 'pg_dumpall: option --exclude-database rejects multipart database names'
+);
+
+#########################################
+# Test valid database exclusion patterns
+
+$node->command_ok(
+ [ 'pg_dumpall', '-p', "$port", '--exclude-database', '"myhost.mydb"' ],
+ 'pg_dumpall: option --exclude-database handles database names with embedded dots'
+);
+
+$node->command_ok(
+ [ 'pg_dumpall', '--exclude-database', '??*' ],
+ 'pg_dumpall: option --exclude-database handles database name patterns'
+);
+
+
+#########################################
+# Test invalid multipart schema names
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', 'myhost.mydb.myschema' ],
+ qr/pg_dump: error: improper qualified name \(too many dotted names\): myhost\.mydb\.myschema/,
+ 'pg_dump: option --schema rejects three-part schema names'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', 'otherdb.myschema' ],
+ qr/pg_dump: error: cross-database references are not implemented: otherdb\.myschema/,
+ 'pg_dump: option --schema rejects cross-database multipart schema names'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', '.' ],
+ qr/pg_dump: error: cross-database references are not implemented: \./,
+ 'pg_dump: option --schema rejects degenerate two-part schema name: "."'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', '"some.other.db".myschema' ],
+ qr/pg_dump: error: cross-database references are not implemented: "some\.other\.db"\.myschema/,
+ 'pg_dump: option --schema rejects cross-database multipart schema names with embedded dots'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', '.*' ],
+ qr/pg_dump: error: cross-database references are not implemented: \.\*/,
+ 'pg_dump: option --schema rejects degenerate two-part schema name: ".*"'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', '..' ],
+ qr/pg_dump: error: improper qualified name \(too many dotted names\): \.\./,
+ 'pg_dump: option --schema rejects degenerate three-part schema name: ".."'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--schema', '.*.*' ],
+ qr/pg_dump: error: improper qualified name \(too many dotted names\): \.\*\.\*/,
+ 'pg_dump: option --schema rejects degenerate three-part schema pattern: ".*.*"'
+);
+
+#########################################
+# Test invalid multipart relation names
+
+$node->command_fails_like(
+ [ 'pg_dump', '--table', 'myhost.mydb.myschema.mytable' ],
+ qr/pg_dump: error: improper relation name \(too many dotted names\): myhost\.mydb\.myschema\.mytable/,
+ 'pg_dump: option --table rejects four-part table names'
+);
+
+$node->command_fails_like(
+ [ 'pg_dump', '--table', 'otherdb.pg_catalog.pg_class' ],
+ qr/pg_dump: error: cross-database references are not implemented: otherdb\.pg_catalog\.pg_class/,
+ 'pg_dump: option --table rejects cross-database three part table names'
+);
+
+command_fails_like(
+ [ 'pg_dump', '-p', "$port", '--table', '"some.other.db".pg_catalog.pg_class' ],
+ qr/pg_dump: error: cross-database references are not implemented: "some\.other\.db"\.pg_catalog\.pg_class/,
+ 'pg_dump: option --table rejects cross-database three part table names with embedded dots'
+);
+
+#########################################
# Run all runs
foreach my $run (sort keys %pgdump_runs)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 583817b0cc6..4369f2235b4 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -46,6 +46,12 @@ static bool describeOneTSConfig(const char *oid, const char *nspname,
const char *pnspname, const char *prsname);
static void printACLColumn(PQExpBuffer buf, const char *colname);
static bool listOneExtensionContents(const char *extname, const char *oid);
+static bool validateSQLNamePattern(PQExpBuffer buf, const char *pattern,
+ bool have_where, bool force_escape,
+ const char *schemavar, const char *namevar,
+ const char *altnamevar,
+ const char *visibilityrule,
+ bool *added_clause, int maxparts);
/*----------------
@@ -102,9 +108,11 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "p.proname", NULL,
- "pg_catalog.pg_function_is_visible(p.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "p.proname", NULL,
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
@@ -170,9 +178,11 @@ describeAccessMethods(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_am\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "amname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "amname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -230,9 +240,11 @@ describeTablespaces(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_tablespace\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "spcname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "spcname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -518,9 +530,11 @@ describeFunctions(const char *functypes, const char *func_pattern,
appendPQExpBufferStr(&buf, " )\n");
}
- processSQLNamePattern(pset.db, &buf, func_pattern, have_where, false,
- "n.nspname", "p.proname", NULL,
- "pg_catalog.pg_function_is_visible(p.oid)");
+ if (!validateSQLNamePattern(&buf, func_pattern, have_where, false,
+ "n.nspname", "p.proname", NULL,
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ NULL, 3))
+ return false;
for (int i = 0; i < num_arg_patterns; i++)
{
@@ -542,10 +556,12 @@ describeFunctions(const char *functypes, const char *func_pattern,
"pg_catalog.format_type(t%d.oid, NULL)", i);
snprintf(tiv, sizeof(tiv),
"pg_catalog.pg_type_is_visible(t%d.oid)", i);
- processSQLNamePattern(pset.db, &buf,
- map_typename_pattern(arg_patterns[i]),
- true, false,
- nspname, typname, ft, tiv);
+ if (!validateSQLNamePattern(&buf,
+ map_typename_pattern(arg_patterns[i]),
+ true, false,
+ nspname, typname, ft, tiv,
+ NULL, 3))
+ return false;
}
else
{
@@ -660,11 +676,13 @@ describeTypes(const char *pattern, bool verbose, bool showSystem)
" AND n.nspname <> 'information_schema'\n");
/* Match name pattern against either internal or external name */
- processSQLNamePattern(pset.db, &buf, map_typename_pattern(pattern),
- true, false,
- "n.nspname", "t.typname",
- "pg_catalog.format_type(t.oid, NULL)",
- "pg_catalog.pg_type_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, map_typename_pattern(pattern),
+ true, false,
+ "n.nspname", "t.typname",
+ "pg_catalog.format_type(t.oid, NULL)",
+ "pg_catalog.pg_type_is_visible(t.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -813,10 +831,12 @@ describeOperators(const char *oper_pattern,
appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, oper_pattern,
- !showSystem && !oper_pattern, true,
- "n.nspname", "o.oprname", NULL,
- "pg_catalog.pg_operator_is_visible(o.oid)");
+ if (!validateSQLNamePattern(&buf, oper_pattern,
+ !showSystem && !oper_pattern, true,
+ "n.nspname", "o.oprname", NULL,
+ "pg_catalog.pg_operator_is_visible(o.oid)",
+ NULL, 3))
+ return false;
if (num_arg_patterns == 1)
appendPQExpBufferStr(&buf, " AND o.oprleft = 0\n");
@@ -841,10 +861,12 @@ describeOperators(const char *oper_pattern,
"pg_catalog.format_type(t%d.oid, NULL)", i);
snprintf(tiv, sizeof(tiv),
"pg_catalog.pg_type_is_visible(t%d.oid)", i);
- processSQLNamePattern(pset.db, &buf,
- map_typename_pattern(arg_patterns[i]),
- true, false,
- nspname, typname, ft, tiv);
+ if (!validateSQLNamePattern(&buf,
+ map_typename_pattern(arg_patterns[i]),
+ true, false,
+ nspname, typname, ft, tiv,
+ NULL, 3))
+ return false;
}
else
{
@@ -928,8 +950,10 @@ listAllDbs(const char *pattern, bool verbose)
" JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
if (pattern)
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "d.datname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "d.datname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data);
@@ -1078,9 +1102,11 @@ permissionsList(const char *pattern)
* point of view. You can see 'em by explicit request though, eg with \z
* pg_catalog.*
*/
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.relname", NULL,
- "n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.relname", NULL,
+ "n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -1145,11 +1171,13 @@ listDefaultACLs(const char *pattern)
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL,
- "n.nspname",
- "pg_catalog.pg_get_userbyid(d.defaclrole)",
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL,
+ "n.nspname",
+ "pg_catalog.pg_get_userbyid(d.defaclrole)",
+ NULL,
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
@@ -1221,9 +1249,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
- false, "n.nspname", "pgc.conname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
+ false, "n.nspname", "pgc.conname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
/* Domain constraint descriptions */
appendPQExpBuffer(&buf,
@@ -1243,9 +1273,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
- false, "n.nspname", "pgc.conname", NULL,
- "pg_catalog.pg_type_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
+ false, "n.nspname", "pgc.conname", NULL,
+ "pg_catalog.pg_type_is_visible(t.oid)",
+ NULL, 3))
+ return false;
/* Operator class descriptions */
appendPQExpBuffer(&buf,
@@ -1265,9 +1297,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "o.opcname", NULL,
- "pg_catalog.pg_opclass_is_visible(o.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "o.opcname", NULL,
+ "pg_catalog.pg_opclass_is_visible(o.oid)",
+ NULL, 3))
+ return false;
/* Operator family descriptions */
appendPQExpBuffer(&buf,
@@ -1287,9 +1321,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "opf.opfname", NULL,
- "pg_catalog.pg_opfamily_is_visible(opf.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "opf.opfname", NULL,
+ "pg_catalog.pg_opfamily_is_visible(opf.oid)",
+ NULL, 3))
+ return false;
/* Rule descriptions (ignore rules for views) */
appendPQExpBuffer(&buf,
@@ -1308,9 +1344,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "r.rulename", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "r.rulename", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
/* Trigger descriptions */
appendPQExpBuffer(&buf,
@@ -1328,9 +1366,11 @@ objectDescription(const char *pattern, bool showSystem)
appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
- "n.nspname", "t.tgname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
+ "n.nspname", "t.tgname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf,
") AS tt\n"
@@ -1384,9 +1424,11 @@ describeTableDetails(const char *pattern, bool verbose, bool showSystem)
appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
- "n.nspname", "c.relname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
+ "n.nspname", "c.relname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
@@ -3572,8 +3614,10 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
if (!showSystem && !pattern)
appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "r.rolname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "r.rolname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -3696,10 +3740,13 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
gettext_noop("Role"),
gettext_noop("Database"),
gettext_noop("Settings"));
- havewhere = processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "r.rolname", NULL, NULL);
- processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false,
- NULL, "d.datname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "r.rolname", NULL, NULL, &havewhere, 1))
+ return false;
+ if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
+ NULL, "d.datname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data);
@@ -3892,9 +3939,11 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
" AND n.nspname !~ '^pg_toast'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.relname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.relname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
@@ -4107,9 +4156,11 @@ listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
" AND n.nspname !~ '^pg_toast'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.relname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.relname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
mixed_output ? "\"Type\" DESC, " : "",
@@ -4182,8 +4233,10 @@ listLanguages(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Description"));
if (pattern)
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "l.lanname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "l.lanname", NULL, NULL,
+ NULL, 2))
+ return false;
if (!showSystem && !pattern)
appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
@@ -4265,9 +4318,11 @@ listDomains(const char *pattern, bool verbose, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "t.typname", NULL,
- "pg_catalog.pg_type_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "t.typname", NULL,
+ "pg_catalog.pg_type_is_visible(t.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -4339,9 +4394,11 @@ listConversions(const char *pattern, bool verbose, bool showSystem)
appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.conname", NULL,
- "pg_catalog.pg_conversion_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.conname", NULL,
+ "pg_catalog.pg_conversion_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -4406,7 +4463,7 @@ describeConfigurationParameters(const char *pattern, bool verbose,
processSQLNamePattern(pset.db, &buf, pattern,
false, false,
NULL, "pg_catalog.lower(s.name)", NULL,
- NULL);
+ NULL, NULL, NULL);
else
appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
" s.setting IS DISTINCT FROM s.boot_val\n");
@@ -4485,8 +4542,10 @@ listEventTriggers(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_event_trigger e ");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "evtname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "evtname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1");
@@ -4577,10 +4636,12 @@ listExtendedStats(const char *pattern)
appendPQExpBufferStr(&buf,
" \nFROM pg_catalog.pg_statistic_ext es \n");
- processSQLNamePattern(pset.db, &buf, pattern,
- false, false,
- "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
- NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)");
+ if (!validateSQLNamePattern(&buf, pattern,
+ false, false,
+ "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
+ NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -4679,17 +4740,21 @@ listCasts(const char *pattern, bool verbose)
* Match name pattern against either internal or external name of either
* castsource or casttarget
*/
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "ns.nspname", "ts.typname",
- "pg_catalog.format_type(ts.oid, NULL)",
- "pg_catalog.pg_type_is_visible(ts.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "ns.nspname", "ts.typname",
+ "pg_catalog.format_type(ts.oid, NULL)",
+ "pg_catalog.pg_type_is_visible(ts.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, ") OR (true");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "nt.nspname", "tt.typname",
- "pg_catalog.format_type(tt.oid, NULL)",
- "pg_catalog.pg_type_is_visible(tt.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "nt.nspname", "tt.typname",
+ "pg_catalog.format_type(tt.oid, NULL)",
+ "pg_catalog.pg_type_is_visible(tt.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
@@ -4785,9 +4850,11 @@ listCollations(const char *pattern, bool verbose, bool showSystem)
*/
appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.collname", NULL,
- "pg_catalog.pg_collation_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.collname", NULL,
+ "pg_catalog.pg_collation_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -4845,10 +4912,12 @@ listSchemas(const char *pattern, bool verbose, bool showSystem)
appendPQExpBufferStr(&buf,
"WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
- processSQLNamePattern(pset.db, &buf, pattern,
- !showSystem && !pattern, false,
- NULL, "n.nspname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern,
+ !showSystem && !pattern, false,
+ NULL, "n.nspname", NULL,
+ NULL,
+ NULL, 2))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -4959,9 +5028,11 @@ listTSParsers(const char *pattern, bool verbose)
gettext_noop("Description")
);
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "p.prsname", NULL,
- "pg_catalog.pg_ts_parser_is_visible(p.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "p.prsname", NULL,
+ "pg_catalog.pg_ts_parser_is_visible(p.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5000,9 +5071,11 @@ listTSParsersVerbose(const char *pattern)
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
);
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "p.prsname", NULL,
- "pg_catalog.pg_ts_parser_is_visible(p.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "p.prsname", NULL,
+ "pg_catalog.pg_ts_parser_is_visible(p.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5207,9 +5280,11 @@ listTSDictionaries(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "d.dictname", NULL,
- "pg_catalog.pg_ts_dict_is_visible(d.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "d.dictname", NULL,
+ "pg_catalog.pg_ts_dict_is_visible(d.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5268,9 +5343,11 @@ listTSTemplates(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "t.tmplname", NULL,
- "pg_catalog.pg_ts_template_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "t.tmplname", NULL,
+ "pg_catalog.pg_ts_template_is_visible(t.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5318,9 +5395,11 @@ listTSConfigs(const char *pattern, bool verbose)
gettext_noop("Description")
);
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "c.cfgname", NULL,
- "pg_catalog.pg_ts_config_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "c.cfgname", NULL,
+ "pg_catalog.pg_ts_config_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5360,9 +5439,11 @@ listTSConfigsVerbose(const char *pattern)
"WHERE p.oid = c.cfgparser\n"
);
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- "n.nspname", "c.cfgname", NULL,
- "pg_catalog.pg_ts_config_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ "n.nspname", "c.cfgname", NULL,
+ "pg_catalog.pg_ts_config_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
@@ -5532,8 +5613,10 @@ listForeignDataWrappers(const char *pattern, bool verbose)
" ON d.classoid = fdw.tableoid "
"AND d.objoid = fdw.oid AND d.objsubid = 0\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "fdwname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "fdwname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -5604,8 +5687,10 @@ listForeignServers(const char *pattern, bool verbose)
"ON d.classoid = s.tableoid AND d.objoid = s.oid "
"AND d.objsubid = 0\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "s.srvname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "s.srvname", NULL, NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -5655,8 +5740,10 @@ listUserMappings(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "um.srvname", "um.usename", NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "um.srvname", "um.usename", NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5722,9 +5809,11 @@ listForeignTables(const char *pattern, bool verbose)
" ON d.classoid = c.tableoid AND "
"d.objoid = c.oid AND d.objsubid = 0\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- "n.nspname", "c.relname", NULL,
- "pg_catalog.pg_table_is_visible(c.oid)");
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ "n.nspname", "c.relname", NULL,
+ "pg_catalog.pg_table_is_visible(c.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
@@ -5768,10 +5857,12 @@ listExtensions(const char *pattern)
gettext_noop("Schema"),
gettext_noop("Description"));
- processSQLNamePattern(pset.db, &buf, pattern,
- false, false,
- NULL, "e.extname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern,
+ false, false,
+ NULL, "e.extname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -5807,10 +5898,12 @@ listExtensionContents(const char *pattern)
"SELECT e.extname, e.oid\n"
"FROM pg_catalog.pg_extension e\n");
- processSQLNamePattern(pset.db, &buf, pattern,
- false, false,
- NULL, "e.extname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern,
+ false, false,
+ NULL, "e.extname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -5893,6 +5986,59 @@ listOneExtensionContents(const char *extname, const char *oid)
}
/*
+ * validateSQLNamePattern
+ *
+ * Wrapper around string_utils's processSQLNamePattern which also checks the
+ * pattern's validity. In addition to that function's parameters, takes a
+ * 'maxparts' parameter specifying the maximum number of dotted names the
+ * pattern is allowed to have, and a 'added_clause' parameter that returns by
+ * reference whether a clause was added to 'buf'. Returns whether the pattern
+ * passed validation, after logging any errors.
+ */
+static bool
+validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where,
+ bool force_escape, const char *schemavar,
+ const char *namevar, const char *altnamevar,
+ const char *visibilityrule, bool *added_clause,
+ int maxparts)
+{
+ PQExpBufferData dbbuf;
+ int dotcnt;
+ bool added;
+
+ initPQExpBuffer(&dbbuf);
+ added = processSQLNamePattern(pset.db, buf, pattern, have_where, force_escape,
+ schemavar, namevar, altnamevar,
+ visibilityrule, &dbbuf, &dotcnt);
+ if (added_clause != NULL)
+ *added_clause = added;
+
+ if (dotcnt >= maxparts)
+ {
+ pg_log_error("improper qualified name (too many dotted names): %s",
+ pattern);
+ termPQExpBuffer(&dbbuf);
+ return false;
+ }
+
+ if (maxparts > 1 && dotcnt == maxparts-1)
+ {
+ if (PQdb(pset.db) == NULL)
+ {
+ pg_log_error("You are currently not connected to a database.");
+ return false;
+ }
+ if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
+ {
+ pg_log_error("cross-database references are not implemented: %s",
+ pattern);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
* \dRp
* Lists publications.
*
@@ -5943,9 +6089,11 @@ listPublications(const char *pattern)
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "pubname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "pubname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -6056,9 +6204,11 @@ describePublications(const char *pattern)
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n");
- processSQLNamePattern(pset.db, &buf, pattern, false, false,
- NULL, "pubname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, false, false,
+ NULL, "pubname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 2;");
@@ -6266,9 +6416,11 @@ describeSubscriptions(const char *pattern, bool verbose)
" FROM pg_catalog.pg_database\n"
" WHERE datname = pg_catalog.current_database())");
- processSQLNamePattern(pset.db, &buf, pattern, true, false,
- NULL, "subname", NULL,
- NULL);
+ if (!validateSQLNamePattern(&buf, pattern, true, false,
+ NULL, "subname", NULL,
+ NULL,
+ NULL, 1))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1;");
@@ -6369,15 +6521,19 @@ listOperatorClasses(const char *access_method_pattern,
" 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 (!validateSQLNamePattern(&buf, access_method_pattern,
+ false, false, NULL, "am.amname", NULL, NULL,
+ &have_where, 1))
+ return false;
if (type_pattern)
{
/* Match type name pattern against either internal or external name */
- processSQLNamePattern(pset.db, &buf, type_pattern, have_where, false,
- "tn.nspname", "t.typname",
- "pg_catalog.format_type(t.oid, NULL)",
- "pg_catalog.pg_type_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, type_pattern, have_where, false,
+ "tn.nspname", "t.typname",
+ "pg_catalog.format_type(t.oid, NULL)",
+ "pg_catalog.pg_type_is_visible(t.oid)",
+ NULL, 3))
+ return false;
}
appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
@@ -6441,8 +6597,10 @@ listOperatorFamilies(const char *access_method_pattern,
" 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 (!validateSQLNamePattern(&buf, access_method_pattern,
+ false, false, NULL, "am.amname", NULL, NULL,
+ &have_where, 1))
+ return false;
if (type_pattern)
{
appendPQExpBuffer(&buf,
@@ -6454,10 +6612,12 @@ listOperatorFamilies(const char *access_method_pattern,
" WHERE oc.opcfamily = f.oid\n",
have_where ? "AND" : "WHERE");
/* Match type name pattern against either internal or external name */
- processSQLNamePattern(pset.db, &buf, type_pattern, true, false,
- "tn.nspname", "t.typname",
- "pg_catalog.format_type(t.oid, NULL)",
- "pg_catalog.pg_type_is_visible(t.oid)");
+ if (!validateSQLNamePattern(&buf, type_pattern, true, false,
+ "tn.nspname", "t.typname",
+ "pg_catalog.format_type(t.oid, NULL)",
+ "pg_catalog.pg_type_is_visible(t.oid)",
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, " )\n");
}
@@ -6535,13 +6695,17 @@ listOpFamilyOperators(const char *access_method_pattern,
" 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 (!validateSQLNamePattern(&buf, access_method_pattern,
+ false, false, NULL, "am.amname",
+ NULL, NULL,
+ &have_where, 1))
+ return false;
if (family_pattern)
- processSQLNamePattern(pset.db, &buf, family_pattern, have_where, false,
- "nsf.nspname", "of.opfname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
+ "nsf.nspname", "of.opfname", NULL, NULL,
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
" o.amoplefttype = o.amoprighttype DESC,\n"
@@ -6619,12 +6783,16 @@ listOpFamilyFunctions(const char *access_method_pattern,
" 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 (!validateSQLNamePattern(&buf, access_method_pattern,
+ false, false, NULL, "am.amname",
+ NULL, NULL,
+ &have_where, 1))
+ return false;
if (family_pattern)
- processSQLNamePattern(pset.db, &buf, family_pattern, have_where, false,
- "ns.nspname", "of.opfname", NULL, NULL);
+ if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
+ "ns.nspname", "of.opfname", NULL, NULL,
+ NULL, 3))
+ return false;
appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
" ap.amproclefttype = ap.amprocrighttype DESC,\n"
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 1c618404627..c3ea4fc1860 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -882,6 +882,9 @@ appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
* altnamevar: NULL, or name of an alternative variable to match against name.
* visibilityrule: clause to use if we want to restrict to visible objects
* (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL.
+ * dbnamebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * dotcnt: how many separators were parsed from the pattern, by reference.
*
* Formatting note: the text already present in buf should end with a newline.
* The appended text, if any, will end with one too.
@@ -890,16 +893,21 @@ bool
processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
bool have_where, bool force_escape,
const char *schemavar, const char *namevar,
- const char *altnamevar, const char *visibilityrule)
+ const char *altnamevar, const char *visibilityrule,
+ PQExpBuffer dbnamebuf, int *dotcnt)
{
PQExpBufferData schemabuf;
PQExpBufferData namebuf;
bool added_clause = false;
+ int dcnt;
#define WHEREAND() \
(appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
have_where = true, added_clause = true)
+ if (dotcnt == NULL)
+ dotcnt = &dcnt;
+ *dotcnt = 0;
if (pattern == NULL)
{
/* Default: select all visible objects */
@@ -922,9 +930,11 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* If the caller provided a schemavar, we want to split the pattern on
* ".", otherwise not.
*/
- patternToSQLRegex(PQclientEncoding(conn), NULL,
- (schemavar ? &schemabuf : NULL), &namebuf,
- pattern, force_escape);
+ patternToSQLRegex(PQclientEncoding(conn),
+ (schemavar ? dbnamebuf : NULL),
+ (schemavar ? &schemabuf : NULL),
+ &namebuf,
+ pattern, force_escape, true, dotcnt);
/*
* Now decide what we need to emit. We may run under a hostile
@@ -937,7 +947,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* is >= v12 then we need to force it through explicit COLLATE clauses,
* otherwise the "C" collation attached to "name" catalog columns wins.
*/
- if (namebuf.len > 2)
+ if (namevar && namebuf.len > 2)
{
/* We have a name pattern, so constrain the namevar(s) */
@@ -971,7 +981,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
}
}
- if (schemabuf.len > 2)
+ if (schemavar && schemabuf.len > 2)
{
/* We have a schema pattern, so constrain the schemavar */
@@ -1012,8 +1022,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* If the dbnamebuf and schemabuf arguments are non-NULL, and the pattern
* contains two or more dbname/schema/name separators, we parse the portions of
* the pattern prior to the first and second separators into dbnamebuf and
- * schemabuf, and the rest into namebuf. (Additional dots in the name portion
- * are not treated as special.)
+ * schemabuf, and the rest into namebuf.
*
* If dbnamebuf is NULL and schemabuf is non-NULL, and the pattern contains at
* least one separator, we parse the first portion into schemabuf and the rest
@@ -1021,24 +1030,49 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
*
* Otherwise, we parse all the pattern into namebuf.
*
+ * If the pattern contains more dotted parts than buffers to parse into, the
+ * extra dots will be treated as literal characters and written into the
+ * namebuf, though they will be counted. Callers should always check the value
+ * returned by reference in dotcnt and handle this error case appropriately.
+ *
* We surround the regexps with "^(...)$" to force them to match whole strings,
* as per SQL practice. We have to have parens in case strings contain "|",
* else the "^" and "$" will be bound into the first and last alternatives
- * which is not what we want.
+ * which is not what we want. Whether this is done for dbnamebuf is controlled
+ * by the want_literal_dbname parameter.
*
* The regexps we parse into the buffers are appended to the data (if any)
* already present. If we parse fewer fields than the number of buffers we
* were given, the extra buffers are unaltered.
+ *
+ * encoding: the character encoding for the given pattern
+ * dbnamebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * schemabuf: output parameter receiving the schema name portion of the
+ * pattern, if any. Can be NULL.
+ * namebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * pattern: user-specified pattern option, or NULL if none ("*" is implied).
+ * force_escape: always quote regexp special characters, even outside
+ * double quotes (else they are quoted only between double quotes).
+ * want_literal_dbname: if true, regexp special characters within the database
+ * name portion of the pattern will not be escaped, nor will the dbname be
+ * converted into a regular expression.
+ * dotcnt: output parameter receiving the number of separators parsed from the
+ * pattern.
*/
void
patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
- PQExpBuffer namebuf, const char *pattern, bool force_escape)
+ PQExpBuffer namebuf, const char *pattern, bool force_escape,
+ bool want_literal_dbname, int *dotcnt)
{
PQExpBufferData buf[3];
+ PQExpBufferData left_literal;
PQExpBuffer curbuf;
PQExpBuffer maxbuf;
int i;
bool inquotes;
+ bool left;
const char *cp;
Assert(pattern != NULL);
@@ -1046,7 +1080,9 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
/* callers should never expect "dbname.relname" format */
Assert(dbnamebuf == NULL || schemabuf != NULL);
+ Assert(dotcnt != NULL);
+ *dotcnt = 0;
inquotes = false;
cp = pattern;
@@ -1058,6 +1094,13 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
maxbuf = &buf[0];
curbuf = &buf[0];
+ if (want_literal_dbname)
+ {
+ left = true;
+ initPQExpBuffer(&left_literal);
+ }
+ else
+ left = false;
initPQExpBuffer(curbuf);
appendPQExpBufferStr(curbuf, "^(");
while (*cp)
@@ -1070,6 +1113,8 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
{
/* emit one quote, stay in inquotes mode */
appendPQExpBufferChar(curbuf, '"');
+ if (left)
+ appendPQExpBufferChar(&left_literal, '"');
cp++;
}
else
@@ -1080,32 +1125,40 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
{
appendPQExpBufferChar(curbuf,
pg_tolower((unsigned char) ch));
+ if (left)
+ appendPQExpBufferChar(&left_literal,
+ pg_tolower((unsigned char) ch));
cp++;
}
else if (!inquotes && ch == '*')
{
appendPQExpBufferStr(curbuf, ".*");
+ if (left)
+ appendPQExpBufferChar(&left_literal, '*');
cp++;
}
else if (!inquotes && ch == '?')
{
appendPQExpBufferChar(curbuf, '.');
+ if (left)
+ appendPQExpBufferChar(&left_literal, '?');
cp++;
}
-
- /*
- * When we find a dbname/schema/name separator, we treat it specially
- * only if the caller requested more patterns to be parsed than we
- * have already parsed from the pattern. Otherwise, dot characters
- * are not special.
- */
- else if (!inquotes && ch == '.' && curbuf < maxbuf)
+ else if (!inquotes && ch == '.')
{
- appendPQExpBufferStr(curbuf, ")$");
- curbuf++;
- initPQExpBuffer(curbuf);
- appendPQExpBufferStr(curbuf, "^(");
- cp++;
+ left = false;
+ if (dotcnt)
+ (*dotcnt)++;
+ if (curbuf < maxbuf)
+ {
+ appendPQExpBufferStr(curbuf, ")$");
+ curbuf++;
+ initPQExpBuffer(curbuf);
+ appendPQExpBufferStr(curbuf, "^(");
+ cp++;
+ }
+ else
+ appendPQExpBufferChar(curbuf, *cp++);
}
else if (ch == '$')
{
@@ -1117,6 +1170,8 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
* having it possess its regexp meaning.
*/
appendPQExpBufferStr(curbuf, "\\$");
+ if (left)
+ appendPQExpBufferChar(&left_literal, '$');
cp++;
}
else
@@ -1141,25 +1196,35 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
appendPQExpBufferChar(curbuf, '\\');
i = PQmblenBounded(cp, encoding);
while (i--)
+ {
+ if (left)
+ appendPQExpBufferChar(&left_literal, *cp);
appendPQExpBufferChar(curbuf, *cp++);
+ }
}
}
appendPQExpBufferStr(curbuf, ")$");
- appendPQExpBufferStr(namebuf, curbuf->data);
- termPQExpBuffer(curbuf);
-
- if (curbuf > buf)
+ if (namebuf)
{
+ appendPQExpBufferStr(namebuf, curbuf->data);
+ termPQExpBuffer(curbuf);
curbuf--;
+ }
+
+ if (schemabuf && curbuf >= buf)
+ {
appendPQExpBufferStr(schemabuf, curbuf->data);
termPQExpBuffer(curbuf);
+ curbuf--;
+ }
- if (curbuf > buf)
- {
- curbuf--;
+ if (dbnamebuf && curbuf >= buf)
+ {
+ if (want_literal_dbname)
+ appendPQExpBufferStr(dbnamebuf, left_literal.data);
+ else
appendPQExpBufferStr(dbnamebuf, curbuf->data);
- termPQExpBuffer(curbuf);
- }
+ termPQExpBuffer(curbuf);
}
}
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h
index b9b8708dab7..fa4deb24978 100644
--- a/src/include/fe_utils/string_utils.h
+++ b/src/include/fe_utils/string_utils.h
@@ -55,10 +55,12 @@ extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
const char *pattern,
bool have_where, bool force_escape,
const char *schemavar, const char *namevar,
- const char *altnamevar, const char *visibilityrule);
+ const char *altnamevar, const char *visibilityrule,
+ PQExpBuffer dbnamebuf, int *dotcnt);
extern void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf,
PQExpBuffer schemabuf, PQExpBuffer namebuf,
- const char *pattern, bool force_escape);
+ const char *pattern, bool force_escape,
+ bool want_literal_dbname, int *dotcnt);
#endif /* STRING_UTILS_H */
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 8e11ebbcaa4..1c5b5d2763d 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5549,3 +5549,807 @@ SELECT * FROM bla ORDER BY 1;
# final ON_ERROR_ROLLBACK: off
DROP TABLE bla;
DROP FUNCTION psql_error;
+-- check describing invalid multipart names
+\dA regression.heap
+improper qualified name (too many dotted names): regression.heap
+\dA nonesuch.heap
+improper qualified name (too many dotted names): nonesuch.heap
+\dt host.regression.pg_catalog.pg_class
+improper qualified name (too many dotted names): host.regression.pg_catalog.pg_class
+\dt |.pg_catalog.pg_class
+cross-database references are not implemented: |.pg_catalog.pg_class
+\dt nonesuch.pg_catalog.pg_class
+cross-database references are not implemented: nonesuch.pg_catalog.pg_class
+\da host.regression.pg_catalog.sum
+improper qualified name (too many dotted names): host.regression.pg_catalog.sum
+\da +.pg_catalog.sum
+cross-database references are not implemented: +.pg_catalog.sum
+\da nonesuch.pg_catalog.sum
+cross-database references are not implemented: nonesuch.pg_catalog.sum
+\dAc nonesuch.brin
+improper qualified name (too many dotted names): nonesuch.brin
+\dAc regression.brin
+improper qualified name (too many dotted names): regression.brin
+\dAf nonesuch.brin
+improper qualified name (too many dotted names): nonesuch.brin
+\dAf regression.brin
+improper qualified name (too many dotted names): regression.brin
+\dAo nonesuch.brin
+improper qualified name (too many dotted names): nonesuch.brin
+\dAo regression.brin
+improper qualified name (too many dotted names): regression.brin
+\dAp nonesuch.brin
+improper qualified name (too many dotted names): nonesuch.brin
+\dAp regression.brin
+improper qualified name (too many dotted names): regression.brin
+\db nonesuch.pg_default
+improper qualified name (too many dotted names): nonesuch.pg_default
+\db regression.pg_default
+improper qualified name (too many dotted names): regression.pg_default
+\dc host.regression.public.conversion
+improper qualified name (too many dotted names): host.regression.public.conversion
+\dc (.public.conversion
+cross-database references are not implemented: (.public.conversion
+\dc nonesuch.public.conversion
+cross-database references are not implemented: nonesuch.public.conversion
+\dC host.regression.pg_catalog.int8
+improper qualified name (too many dotted names): host.regression.pg_catalog.int8
+\dC ).pg_catalog.int8
+cross-database references are not implemented: ).pg_catalog.int8
+\dC nonesuch.pg_catalog.int8
+cross-database references are not implemented: nonesuch.pg_catalog.int8
+\dd host.regression.pg_catalog.pg_class
+improper qualified name (too many dotted names): host.regression.pg_catalog.pg_class
+\dd [.pg_catalog.pg_class
+cross-database references are not implemented: [.pg_catalog.pg_class
+\dd nonesuch.pg_catalog.pg_class
+cross-database references are not implemented: nonesuch.pg_catalog.pg_class
+\dD host.regression.public.gtestdomain1
+improper qualified name (too many dotted names): host.regression.public.gtestdomain1
+\dD ].public.gtestdomain1
+cross-database references are not implemented: ].public.gtestdomain1
+\dD nonesuch.public.gtestdomain1
+cross-database references are not implemented: nonesuch.public.gtestdomain1
+\ddp host.regression.pg_catalog.pg_class
+improper qualified name (too many dotted names): host.regression.pg_catalog.pg_class
+\ddp {.pg_catalog.pg_class
+cross-database references are not implemented: {.pg_catalog.pg_class
+\ddp nonesuch.pg_catalog.pg_class
+cross-database references are not implemented: nonesuch.pg_catalog.pg_class
+\dE host.regression.public.ft
+improper qualified name (too many dotted names): host.regression.public.ft
+\dE }.public.ft
+cross-database references are not implemented: }.public.ft
+\dE nonesuch.public.ft
+cross-database references are not implemented: nonesuch.public.ft
+\di host.regression.public.tenk1_hundred
+improper qualified name (too many dotted names): host.regression.public.tenk1_hundred
+\di ..public.tenk1_hundred
+improper qualified name (too many dotted names): ..public.tenk1_hundred
+\di nonesuch.public.tenk1_hundred
+cross-database references are not implemented: nonesuch.public.tenk1_hundred
+\dm host.regression.public.mvtest_bb
+improper qualified name (too many dotted names): host.regression.public.mvtest_bb
+\dm ^.public.mvtest_bb
+cross-database references are not implemented: ^.public.mvtest_bb
+\dm nonesuch.public.mvtest_bb
+cross-database references are not implemented: nonesuch.public.mvtest_bb
+\ds host.regression.public.check_seq
+improper qualified name (too many dotted names): host.regression.public.check_seq
+\ds regression|mydb.public.check_seq
+cross-database references are not implemented: regression|mydb.public.check_seq
+\ds nonesuch.public.check_seq
+cross-database references are not implemented: nonesuch.public.check_seq
+\dt host.regression.public.b_star
+improper qualified name (too many dotted names): host.regression.public.b_star
+\dt regres+ion.public.b_star
+cross-database references are not implemented: regres+ion.public.b_star
+\dt nonesuch.public.b_star
+cross-database references are not implemented: nonesuch.public.b_star
+\dv host.regression.public.shoe
+improper qualified name (too many dotted names): host.regression.public.shoe
+\dv regress(ion).public.shoe
+cross-database references are not implemented: regress(ion).public.shoe
+\dv nonesuch.public.shoe
+cross-database references are not implemented: nonesuch.public.shoe
+\des nonesuch.server
+improper qualified name (too many dotted names): nonesuch.server
+\des regression.server
+improper qualified name (too many dotted names): regression.server
+\des nonesuch.server
+improper qualified name (too many dotted names): nonesuch.server
+\des regression.server
+improper qualified name (too many dotted names): regression.server
+\des nonesuch.username
+improper qualified name (too many dotted names): nonesuch.username
+\des regression.username
+improper qualified name (too many dotted names): regression.username
+\dew nonesuch.fdw
+improper qualified name (too many dotted names): nonesuch.fdw
+\dew regression.fdw
+improper qualified name (too many dotted names): regression.fdw
+\df host.regression.public.namelen
+improper qualified name (too many dotted names): host.regression.public.namelen
+\df regres[qrstuv]ion.public.namelen
+cross-database references are not implemented: regres[qrstuv]ion.public.namelen
+\df nonesuch.public.namelen
+cross-database references are not implemented: nonesuch.public.namelen
+\dF host.regression.pg_catalog.arabic
+improper qualified name (too many dotted names): host.regression.pg_catalog.arabic
+\dF regres{1,2}ion.pg_catalog.arabic
+cross-database references are not implemented: regres{1,2}ion.pg_catalog.arabic
+\dF nonesuch.pg_catalog.arabic
+cross-database references are not implemented: nonesuch.pg_catalog.arabic
+\dFd host.regression.pg_catalog.arabic_stem
+improper qualified name (too many dotted names): host.regression.pg_catalog.arabic_stem
+\dFd regres?ion.pg_catalog.arabic_stem
+cross-database references are not implemented: regres?ion.pg_catalog.arabic_stem
+\dFd nonesuch.pg_catalog.arabic_stem
+cross-database references are not implemented: nonesuch.pg_catalog.arabic_stem
+\dFp host.regression.pg_catalog.default
+improper qualified name (too many dotted names): host.regression.pg_catalog.default
+\dFp ^regression.pg_catalog.default
+cross-database references are not implemented: ^regression.pg_catalog.default
+\dFp nonesuch.pg_catalog.default
+cross-database references are not implemented: nonesuch.pg_catalog.default
+\dFt host.regression.pg_catalog.ispell
+improper qualified name (too many dotted names): host.regression.pg_catalog.ispell
+\dFt regression$.pg_catalog.ispell
+cross-database references are not implemented: regression$.pg_catalog.ispell
+\dFt nonesuch.pg_catalog.ispell
+cross-database references are not implemented: nonesuch.pg_catalog.ispell
+\dg nonesuch.pg_database_owner
+improper qualified name (too many dotted names): nonesuch.pg_database_owner
+\dg regression.pg_database_owner
+improper qualified name (too many dotted names): regression.pg_database_owner
+\dL host.regression.plpgsql
+improper qualified name (too many dotted names): host.regression.plpgsql
+\dL *.plpgsql
+cross-database references are not implemented: *.plpgsql
+\dL nonesuch.plpgsql
+cross-database references are not implemented: nonesuch.plpgsql
+\dn host.regression.public
+improper qualified name (too many dotted names): host.regression.public
+\dn """".public
+cross-database references are not implemented: """".public
+\dn nonesuch.public
+cross-database references are not implemented: nonesuch.public
+\do host.regression.public.!=-
+improper qualified name (too many dotted names): host.regression.public.!=-
+\do "regression|mydb".public.!=-
+cross-database references are not implemented: "regression|mydb".public.!=-
+\do nonesuch.public.!=-
+cross-database references are not implemented: nonesuch.public.!=-
+\dO host.regression.pg_catalog.POSIX
+improper qualified name (too many dotted names): host.regression.pg_catalog.POSIX
+\dO .pg_catalog.POSIX
+cross-database references are not implemented: .pg_catalog.POSIX
+\dO nonesuch.pg_catalog.POSIX
+cross-database references are not implemented: nonesuch.pg_catalog.POSIX
+\dp host.regression.public.a_star
+improper qualified name (too many dotted names): host.regression.public.a_star
+\dp "regres+ion".public.a_star
+cross-database references are not implemented: "regres+ion".public.a_star
+\dp nonesuch.public.a_star
+cross-database references are not implemented: nonesuch.public.a_star
+\dP host.regression.public.mlparted
+improper qualified name (too many dotted names): host.regression.public.mlparted
+\dP "regres(sion)".public.mlparted
+cross-database references are not implemented: "regres(sion)".public.mlparted
+\dP nonesuch.public.mlparted
+cross-database references are not implemented: nonesuch.public.mlparted
+\drds nonesuch.lc_messages
+improper qualified name (too many dotted names): nonesuch.lc_messages
+\drds regression.lc_messages
+improper qualified name (too many dotted names): regression.lc_messages
+\dRp public.mypub
+improper qualified name (too many dotted names): public.mypub
+\dRp regression.mypub
+improper qualified name (too many dotted names): regression.mypub
+\dRs public.mysub
+improper qualified name (too many dotted names): public.mysub
+\dRs regression.mysub
+improper qualified name (too many dotted names): regression.mysub
+\dT host.regression.public.widget
+improper qualified name (too many dotted names): host.regression.public.widget
+\dT "regression{1,2}".public.widget
+cross-database references are not implemented: "regression{1,2}".public.widget
+\dT nonesuch.public.widget
+cross-database references are not implemented: nonesuch.public.widget
+\dx regression.plpgsql
+improper qualified name (too many dotted names): regression.plpgsql
+\dx nonesuch.plpgsql
+improper qualified name (too many dotted names): nonesuch.plpgsql
+\dX host.regression.public.func_deps_stat
+improper qualified name (too many dotted names): host.regression.public.func_deps_stat
+\dX "^regression$".public.func_deps_stat
+cross-database references are not implemented: "^regression$".public.func_deps_stat
+\dX nonesuch.public.func_deps_stat
+cross-database references are not implemented: nonesuch.public.func_deps_stat
+\dy regression.myevt
+improper qualified name (too many dotted names): regression.myevt
+\dy nonesuch.myevt
+improper qualified name (too many dotted names): nonesuch.myevt
+-- check that dots within quoted name segments are not counted
+\dA "no.such.access.method"
+List of access methods
+ Name | Type
+------+------
+(0 rows)
+
+\dt "no.such.table.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\da "no.such.aggregate.function"
+ List of aggregate functions
+ Schema | Name | Result data type | Argument data types | Description
+--------+------+------------------+---------------------+-------------
+(0 rows)
+
+\dAc "no.such.operator.class"
+ List of operator classes
+ AM | Input type | Storage type | Operator class | Default?
+----+------------+--------------+----------------+----------
+(0 rows)
+
+\dAf "no.such.operator.family"
+ List of operator families
+ AM | Operator family | Applicable types
+----+-----------------+------------------
+(0 rows)
+
+\dAo "no.such.operator.of.operator.family"
+ List of operators of operator families
+ AM | Operator family | Operator | Strategy | Purpose
+----+-----------------+----------+----------+---------
+(0 rows)
+
+\dAp "no.such.operator.support.function.of.operator.family"
+ List of support functions of operator families
+ AM | Operator family | Registered left type | Registered right type | Number | Function
+----+-----------------+----------------------+-----------------------+--------+----------
+(0 rows)
+
+\db "no.such.tablespace"
+ List of tablespaces
+ Name | Owner | Location
+------+-------+----------
+(0 rows)
+
+\dc "no.such.conversion"
+ List of conversions
+ Schema | Name | Source | Destination | Default?
+--------+------+--------+-------------+----------
+(0 rows)
+
+\dC "no.such.cast"
+ List of casts
+ Source type | Target type | Function | Implicit?
+-------------+-------------+----------+-----------
+(0 rows)
+
+\dd "no.such.object.description"
+ Object descriptions
+ Schema | Name | Object | Description
+--------+------+--------+-------------
+(0 rows)
+
+\dD "no.such.domain"
+ List of domains
+ Schema | Name | Type | Collation | Nullable | Default | Check
+--------+------+------+-----------+----------+---------+-------
+(0 rows)
+
+\ddp "no.such.default.access.privilege"
+ Default access privileges
+ Owner | Schema | Type | Access privileges
+-------+--------+------+-------------------
+(0 rows)
+
+\di "no.such.index.relation"
+ List of relations
+ Schema | Name | Type | Owner | Table
+--------+------+------+-------+-------
+(0 rows)
+
+\dm "no.such.materialized.view"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\ds "no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dt "no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dv "no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\des "no.such.foreign.server"
+ List of foreign servers
+ Name | Owner | Foreign-data wrapper
+------+-------+----------------------
+(0 rows)
+
+\dew "no.such.foreign.data.wrapper"
+ List of foreign-data wrappers
+ Name | Owner | Handler | Validator
+------+-------+---------+-----------
+(0 rows)
+
+\df "no.such.function"
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\dF "no.such.text.search.configuration"
+List of text search configurations
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFd "no.such.text.search.dictionary"
+List of text search dictionaries
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFp "no.such.text.search.parser"
+ List of text search parsers
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFt "no.such.text.search.template"
+List of text search templates
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dg "no.such.role"
+ List of roles
+ Role name | Attributes | Member of
+-----------+------------+-----------
+
+\dL "no.such.language"
+ List of languages
+ Name | Owner | Trusted | Description
+------+-------+---------+-------------
+(0 rows)
+
+\dn "no.such.schema"
+List of schemas
+ Name | Owner
+------+-------
+(0 rows)
+
+\do "no.such.operator"
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+\dO "no.such.collation"
+ List of collations
+ Schema | Name | Collate | Ctype | ICU Locale | Provider | Deterministic?
+--------+------+---------+-------+------------+----------+----------------
+(0 rows)
+
+\dp "no.such.access.privilege"
+ Access privileges
+ Schema | Name | Type | Access privileges | Column privileges | Policies
+--------+------+------+-------------------+-------------------+----------
+(0 rows)
+
+\dP "no.such.partitioned.relation"
+ List of partitioned relations
+ Schema | Name | Owner | Type | Parent name | Table
+--------+------+-------+------+-------------+-------
+(0 rows)
+
+\drds "no.such.setting"
+ List of settings
+ Role | Database | Settings
+------+----------+----------
+(0 rows)
+
+\dRp "no.such.publication"
+ List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
+------+-------+------------+---------+---------+---------+-----------+----------
+(0 rows)
+
+\dRs "no.such.subscription"
+ List of subscriptions
+ Name | Owner | Enabled | Publication
+------+-------+---------+-------------
+(0 rows)
+
+\dT "no.such.data.type"
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dx "no.such.installed.extension"
+ List of installed extensions
+ Name | Version | Schema | Description
+------+---------+--------+-------------
+(0 rows)
+
+\dX "no.such.extended.statistics"
+ List of extended statistics
+ Schema | Name | Definition | Ndistinct | Dependencies | MCV
+--------+------+------------+-----------+--------------+-----
+(0 rows)
+
+\dy "no.such.event.trigger"
+ List of event triggers
+ Name | Event | Owner | Enabled | Function | Tags
+------+-------+-------+---------+----------+------
+(0 rows)
+
+-- again, but with dotted schema qualifications.
+\dA "no.such.schema"."no.such.access.method"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.access.method"
+\dt "no.such.schema"."no.such.table.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\da "no.such.schema"."no.such.aggregate.function"
+ List of aggregate functions
+ Schema | Name | Result data type | Argument data types | Description
+--------+------+------------------+---------------------+-------------
+(0 rows)
+
+\dAc "no.such.schema"."no.such.operator.class"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.operator.class"
+\dAf "no.such.schema"."no.such.operator.family"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.operator.family"
+\dAo "no.such.schema"."no.such.operator.of.operator.family"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.operator.of.operator.family"
+\dAp "no.such.schema"."no.such.operator.support.function.of.operator.family"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.operator.support.function.of.operator.family"
+\db "no.such.schema"."no.such.tablespace"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.tablespace"
+\dc "no.such.schema"."no.such.conversion"
+ List of conversions
+ Schema | Name | Source | Destination | Default?
+--------+------+--------+-------------+----------
+(0 rows)
+
+\dC "no.such.schema"."no.such.cast"
+ List of casts
+ Source type | Target type | Function | Implicit?
+-------------+-------------+----------+-----------
+(0 rows)
+
+\dd "no.such.schema"."no.such.object.description"
+ Object descriptions
+ Schema | Name | Object | Description
+--------+------+--------+-------------
+(0 rows)
+
+\dD "no.such.schema"."no.such.domain"
+ List of domains
+ Schema | Name | Type | Collation | Nullable | Default | Check
+--------+------+------+-----------+----------+---------+-------
+(0 rows)
+
+\ddp "no.such.schema"."no.such.default.access.privilege"
+ Default access privileges
+ Owner | Schema | Type | Access privileges
+-------+--------+------+-------------------
+(0 rows)
+
+\di "no.such.schema"."no.such.index.relation"
+ List of relations
+ Schema | Name | Type | Owner | Table
+--------+------+------+-------+-------
+(0 rows)
+
+\dm "no.such.schema"."no.such.materialized.view"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\ds "no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dt "no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dv "no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\des "no.such.schema"."no.such.foreign.server"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.foreign.server"
+\dew "no.such.schema"."no.such.foreign.data.wrapper"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.foreign.data.wrapper"
+\df "no.such.schema"."no.such.function"
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\dF "no.such.schema"."no.such.text.search.configuration"
+List of text search configurations
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFd "no.such.schema"."no.such.text.search.dictionary"
+List of text search dictionaries
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFp "no.such.schema"."no.such.text.search.parser"
+ List of text search parsers
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFt "no.such.schema"."no.such.text.search.template"
+List of text search templates
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dg "no.such.schema"."no.such.role"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.role"
+\dL "no.such.schema"."no.such.language"
+cross-database references are not implemented: "no.such.schema"."no.such.language"
+\do "no.such.schema"."no.such.operator"
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+\dO "no.such.schema"."no.such.collation"
+ List of collations
+ Schema | Name | Collate | Ctype | ICU Locale | Provider | Deterministic?
+--------+------+---------+-------+------------+----------+----------------
+(0 rows)
+
+\dp "no.such.schema"."no.such.access.privilege"
+ Access privileges
+ Schema | Name | Type | Access privileges | Column privileges | Policies
+--------+------+------+-------------------+-------------------+----------
+(0 rows)
+
+\dP "no.such.schema"."no.such.partitioned.relation"
+ List of partitioned relations
+ Schema | Name | Owner | Type | Parent name | Table
+--------+------+-------+------+-------------+-------
+(0 rows)
+
+\drds "no.such.schema"."no.such.setting"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.setting"
+\dRp "no.such.schema"."no.such.publication"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.publication"
+\dRs "no.such.schema"."no.such.subscription"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.subscription"
+\dT "no.such.schema"."no.such.data.type"
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dx "no.such.schema"."no.such.installed.extension"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.installed.extension"
+\dX "no.such.schema"."no.such.extended.statistics"
+ List of extended statistics
+ Schema | Name | Definition | Ndistinct | Dependencies | MCV
+--------+------+------------+-----------+--------------+-----
+(0 rows)
+
+\dy "no.such.schema"."no.such.event.trigger"
+improper qualified name (too many dotted names): "no.such.schema"."no.such.event.trigger"
+-- again, but with current database and dotted schema qualifications.
+\dt regression."no.such.schema"."no.such.table.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\da regression."no.such.schema"."no.such.aggregate.function"
+ List of aggregate functions
+ Schema | Name | Result data type | Argument data types | Description
+--------+------+------------------+---------------------+-------------
+(0 rows)
+
+\dc regression."no.such.schema"."no.such.conversion"
+ List of conversions
+ Schema | Name | Source | Destination | Default?
+--------+------+--------+-------------+----------
+(0 rows)
+
+\dC regression."no.such.schema"."no.such.cast"
+ List of casts
+ Source type | Target type | Function | Implicit?
+-------------+-------------+----------+-----------
+(0 rows)
+
+\dd regression."no.such.schema"."no.such.object.description"
+ Object descriptions
+ Schema | Name | Object | Description
+--------+------+--------+-------------
+(0 rows)
+
+\dD regression."no.such.schema"."no.such.domain"
+ List of domains
+ Schema | Name | Type | Collation | Nullable | Default | Check
+--------+------+------+-----------+----------+---------+-------
+(0 rows)
+
+\di regression."no.such.schema"."no.such.index.relation"
+ List of relations
+ Schema | Name | Type | Owner | Table
+--------+------+------+-------+-------
+(0 rows)
+
+\dm regression."no.such.schema"."no.such.materialized.view"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\ds regression."no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dt regression."no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\dv regression."no.such.schema"."no.such.relation"
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
+\df regression."no.such.schema"."no.such.function"
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\dF regression."no.such.schema"."no.such.text.search.configuration"
+List of text search configurations
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFd regression."no.such.schema"."no.such.text.search.dictionary"
+List of text search dictionaries
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFp regression."no.such.schema"."no.such.text.search.parser"
+ List of text search parsers
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dFt regression."no.such.schema"."no.such.text.search.template"
+List of text search templates
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\do regression."no.such.schema"."no.such.operator"
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+\dO regression."no.such.schema"."no.such.collation"
+ List of collations
+ Schema | Name | Collate | Ctype | ICU Locale | Provider | Deterministic?
+--------+------+---------+-------+------------+----------+----------------
+(0 rows)
+
+\dp regression."no.such.schema"."no.such.access.privilege"
+ Access privileges
+ Schema | Name | Type | Access privileges | Column privileges | Policies
+--------+------+------+-------------------+-------------------+----------
+(0 rows)
+
+\dP regression."no.such.schema"."no.such.partitioned.relation"
+ List of partitioned relations
+ Schema | Name | Owner | Type | Parent name | Table
+--------+------+-------+------+-------------+-------
+(0 rows)
+
+\dT regression."no.such.schema"."no.such.data.type"
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\dX regression."no.such.schema"."no.such.extended.statistics"
+ List of extended statistics
+ Schema | Name | Definition | Ndistinct | Dependencies | MCV
+--------+------+------------+-----------+--------------+-----
+(0 rows)
+
+-- again, but with dotted database and dotted schema qualifications.
+\dt "no.such.database"."no.such.schema"."no.such.table.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.table.relation"
+\da "no.such.database"."no.such.schema"."no.such.aggregate.function"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.aggregate.function"
+\dc "no.such.database"."no.such.schema"."no.such.conversion"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.conversion"
+\dC "no.such.database"."no.such.schema"."no.such.cast"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.cast"
+\dd "no.such.database"."no.such.schema"."no.such.object.description"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.object.description"
+\dD "no.such.database"."no.such.schema"."no.such.domain"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.domain"
+\ddp "no.such.database"."no.such.schema"."no.such.default.access.privilege"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.default.access.privilege"
+\di "no.such.database"."no.such.schema"."no.such.index.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.index.relation"
+\dm "no.such.database"."no.such.schema"."no.such.materialized.view"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.materialized.view"
+\ds "no.such.database"."no.such.schema"."no.such.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.relation"
+\dt "no.such.database"."no.such.schema"."no.such.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.relation"
+\dv "no.such.database"."no.such.schema"."no.such.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.relation"
+\df "no.such.database"."no.such.schema"."no.such.function"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.function"
+\dF "no.such.database"."no.such.schema"."no.such.text.search.configuration"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.text.search.configuration"
+\dFd "no.such.database"."no.such.schema"."no.such.text.search.dictionary"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.text.search.dictionary"
+\dFp "no.such.database"."no.such.schema"."no.such.text.search.parser"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.text.search.parser"
+\dFt "no.such.database"."no.such.schema"."no.such.text.search.template"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.text.search.template"
+\do "no.such.database"."no.such.schema"."no.such.operator"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.operator"
+\dO "no.such.database"."no.such.schema"."no.such.collation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.collation"
+\dp "no.such.database"."no.such.schema"."no.such.access.privilege"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.access.privilege"
+\dP "no.such.database"."no.such.schema"."no.such.partitioned.relation"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.partitioned.relation"
+\dT "no.such.database"."no.such.schema"."no.such.data.type"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.data.type"
+\dX "no.such.database"."no.such.schema"."no.such.extended.statistics"
+cross-database references are not implemented: "no.such.database"."no.such.schema"."no.such.extended.statistics"
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index bf372c37a56..6fc0ac6bd17 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -1463,3 +1463,245 @@ SELECT * FROM bla ORDER BY 1;
\echo '# final ON_ERROR_ROLLBACK:' :ON_ERROR_ROLLBACK
DROP TABLE bla;
DROP FUNCTION psql_error;
+
+-- check describing invalid multipart names
+\dA regression.heap
+\dA nonesuch.heap
+\dt host.regression.pg_catalog.pg_class
+\dt |.pg_catalog.pg_class
+\dt nonesuch.pg_catalog.pg_class
+\da host.regression.pg_catalog.sum
+\da +.pg_catalog.sum
+\da nonesuch.pg_catalog.sum
+\dAc nonesuch.brin
+\dAc regression.brin
+\dAf nonesuch.brin
+\dAf regression.brin
+\dAo nonesuch.brin
+\dAo regression.brin
+\dAp nonesuch.brin
+\dAp regression.brin
+\db nonesuch.pg_default
+\db regression.pg_default
+\dc host.regression.public.conversion
+\dc (.public.conversion
+\dc nonesuch.public.conversion
+\dC host.regression.pg_catalog.int8
+\dC ).pg_catalog.int8
+\dC nonesuch.pg_catalog.int8
+\dd host.regression.pg_catalog.pg_class
+\dd [.pg_catalog.pg_class
+\dd nonesuch.pg_catalog.pg_class
+\dD host.regression.public.gtestdomain1
+\dD ].public.gtestdomain1
+\dD nonesuch.public.gtestdomain1
+\ddp host.regression.pg_catalog.pg_class
+\ddp {.pg_catalog.pg_class
+\ddp nonesuch.pg_catalog.pg_class
+\dE host.regression.public.ft
+\dE }.public.ft
+\dE nonesuch.public.ft
+\di host.regression.public.tenk1_hundred
+\di ..public.tenk1_hundred
+\di nonesuch.public.tenk1_hundred
+\dm host.regression.public.mvtest_bb
+\dm ^.public.mvtest_bb
+\dm nonesuch.public.mvtest_bb
+\ds host.regression.public.check_seq
+\ds regression|mydb.public.check_seq
+\ds nonesuch.public.check_seq
+\dt host.regression.public.b_star
+\dt regres+ion.public.b_star
+\dt nonesuch.public.b_star
+\dv host.regression.public.shoe
+\dv regress(ion).public.shoe
+\dv nonesuch.public.shoe
+\des nonesuch.server
+\des regression.server
+\des nonesuch.server
+\des regression.server
+\des nonesuch.username
+\des regression.username
+\dew nonesuch.fdw
+\dew regression.fdw
+\df host.regression.public.namelen
+\df regres[qrstuv]ion.public.namelen
+\df nonesuch.public.namelen
+\dF host.regression.pg_catalog.arabic
+\dF regres{1,2}ion.pg_catalog.arabic
+\dF nonesuch.pg_catalog.arabic
+\dFd host.regression.pg_catalog.arabic_stem
+\dFd regres?ion.pg_catalog.arabic_stem
+\dFd nonesuch.pg_catalog.arabic_stem
+\dFp host.regression.pg_catalog.default
+\dFp ^regression.pg_catalog.default
+\dFp nonesuch.pg_catalog.default
+\dFt host.regression.pg_catalog.ispell
+\dFt regression$.pg_catalog.ispell
+\dFt nonesuch.pg_catalog.ispell
+\dg nonesuch.pg_database_owner
+\dg regression.pg_database_owner
+\dL host.regression.plpgsql
+\dL *.plpgsql
+\dL nonesuch.plpgsql
+\dn host.regression.public
+\dn """".public
+\dn nonesuch.public
+\do host.regression.public.!=-
+\do "regression|mydb".public.!=-
+\do nonesuch.public.!=-
+\dO host.regression.pg_catalog.POSIX
+\dO .pg_catalog.POSIX
+\dO nonesuch.pg_catalog.POSIX
+\dp host.regression.public.a_star
+\dp "regres+ion".public.a_star
+\dp nonesuch.public.a_star
+\dP host.regression.public.mlparted
+\dP "regres(sion)".public.mlparted
+\dP nonesuch.public.mlparted
+\drds nonesuch.lc_messages
+\drds regression.lc_messages
+\dRp public.mypub
+\dRp regression.mypub
+\dRs public.mysub
+\dRs regression.mysub
+\dT host.regression.public.widget
+\dT "regression{1,2}".public.widget
+\dT nonesuch.public.widget
+\dx regression.plpgsql
+\dx nonesuch.plpgsql
+\dX host.regression.public.func_deps_stat
+\dX "^regression$".public.func_deps_stat
+\dX nonesuch.public.func_deps_stat
+\dy regression.myevt
+\dy nonesuch.myevt
+
+-- check that dots within quoted name segments are not counted
+\dA "no.such.access.method"
+\dt "no.such.table.relation"
+\da "no.such.aggregate.function"
+\dAc "no.such.operator.class"
+\dAf "no.such.operator.family"
+\dAo "no.such.operator.of.operator.family"
+\dAp "no.such.operator.support.function.of.operator.family"
+\db "no.such.tablespace"
+\dc "no.such.conversion"
+\dC "no.such.cast"
+\dd "no.such.object.description"
+\dD "no.such.domain"
+\ddp "no.such.default.access.privilege"
+\di "no.such.index.relation"
+\dm "no.such.materialized.view"
+\ds "no.such.relation"
+\dt "no.such.relation"
+\dv "no.such.relation"
+\des "no.such.foreign.server"
+\dew "no.such.foreign.data.wrapper"
+\df "no.such.function"
+\dF "no.such.text.search.configuration"
+\dFd "no.such.text.search.dictionary"
+\dFp "no.such.text.search.parser"
+\dFt "no.such.text.search.template"
+\dg "no.such.role"
+\dL "no.such.language"
+\dn "no.such.schema"
+\do "no.such.operator"
+\dO "no.such.collation"
+\dp "no.such.access.privilege"
+\dP "no.such.partitioned.relation"
+\drds "no.such.setting"
+\dRp "no.such.publication"
+\dRs "no.such.subscription"
+\dT "no.such.data.type"
+\dx "no.such.installed.extension"
+\dX "no.such.extended.statistics"
+\dy "no.such.event.trigger"
+
+-- again, but with dotted schema qualifications.
+\dA "no.such.schema"."no.such.access.method"
+\dt "no.such.schema"."no.such.table.relation"
+\da "no.such.schema"."no.such.aggregate.function"
+\dAc "no.such.schema"."no.such.operator.class"
+\dAf "no.such.schema"."no.such.operator.family"
+\dAo "no.such.schema"."no.such.operator.of.operator.family"
+\dAp "no.such.schema"."no.such.operator.support.function.of.operator.family"
+\db "no.such.schema"."no.such.tablespace"
+\dc "no.such.schema"."no.such.conversion"
+\dC "no.such.schema"."no.such.cast"
+\dd "no.such.schema"."no.such.object.description"
+\dD "no.such.schema"."no.such.domain"
+\ddp "no.such.schema"."no.such.default.access.privilege"
+\di "no.such.schema"."no.such.index.relation"
+\dm "no.such.schema"."no.such.materialized.view"
+\ds "no.such.schema"."no.such.relation"
+\dt "no.such.schema"."no.such.relation"
+\dv "no.such.schema"."no.such.relation"
+\des "no.such.schema"."no.such.foreign.server"
+\dew "no.such.schema"."no.such.foreign.data.wrapper"
+\df "no.such.schema"."no.such.function"
+\dF "no.such.schema"."no.such.text.search.configuration"
+\dFd "no.such.schema"."no.such.text.search.dictionary"
+\dFp "no.such.schema"."no.such.text.search.parser"
+\dFt "no.such.schema"."no.such.text.search.template"
+\dg "no.such.schema"."no.such.role"
+\dL "no.such.schema"."no.such.language"
+\do "no.such.schema"."no.such.operator"
+\dO "no.such.schema"."no.such.collation"
+\dp "no.such.schema"."no.such.access.privilege"
+\dP "no.such.schema"."no.such.partitioned.relation"
+\drds "no.such.schema"."no.such.setting"
+\dRp "no.such.schema"."no.such.publication"
+\dRs "no.such.schema"."no.such.subscription"
+\dT "no.such.schema"."no.such.data.type"
+\dx "no.such.schema"."no.such.installed.extension"
+\dX "no.such.schema"."no.such.extended.statistics"
+\dy "no.such.schema"."no.such.event.trigger"
+
+-- again, but with current database and dotted schema qualifications.
+\dt regression."no.such.schema"."no.such.table.relation"
+\da regression."no.such.schema"."no.such.aggregate.function"
+\dc regression."no.such.schema"."no.such.conversion"
+\dC regression."no.such.schema"."no.such.cast"
+\dd regression."no.such.schema"."no.such.object.description"
+\dD regression."no.such.schema"."no.such.domain"
+\di regression."no.such.schema"."no.such.index.relation"
+\dm regression."no.such.schema"."no.such.materialized.view"
+\ds regression."no.such.schema"."no.such.relation"
+\dt regression."no.such.schema"."no.such.relation"
+\dv regression."no.such.schema"."no.such.relation"
+\df regression."no.such.schema"."no.such.function"
+\dF regression."no.such.schema"."no.such.text.search.configuration"
+\dFd regression."no.such.schema"."no.such.text.search.dictionary"
+\dFp regression."no.such.schema"."no.such.text.search.parser"
+\dFt regression."no.such.schema"."no.such.text.search.template"
+\do regression."no.such.schema"."no.such.operator"
+\dO regression."no.such.schema"."no.such.collation"
+\dp regression."no.such.schema"."no.such.access.privilege"
+\dP regression."no.such.schema"."no.such.partitioned.relation"
+\dT regression."no.such.schema"."no.such.data.type"
+\dX regression."no.such.schema"."no.such.extended.statistics"
+
+-- again, but with dotted database and dotted schema qualifications.
+\dt "no.such.database"."no.such.schema"."no.such.table.relation"
+\da "no.such.database"."no.such.schema"."no.such.aggregate.function"
+\dc "no.such.database"."no.such.schema"."no.such.conversion"
+\dC "no.such.database"."no.such.schema"."no.such.cast"
+\dd "no.such.database"."no.such.schema"."no.such.object.description"
+\dD "no.such.database"."no.such.schema"."no.such.domain"
+\ddp "no.such.database"."no.such.schema"."no.such.default.access.privilege"
+\di "no.such.database"."no.such.schema"."no.such.index.relation"
+\dm "no.such.database"."no.such.schema"."no.such.materialized.view"
+\ds "no.such.database"."no.such.schema"."no.such.relation"
+\dt "no.such.database"."no.such.schema"."no.such.relation"
+\dv "no.such.database"."no.such.schema"."no.such.relation"
+\df "no.such.database"."no.such.schema"."no.such.function"
+\dF "no.such.database"."no.such.schema"."no.such.text.search.configuration"
+\dFd "no.such.database"."no.such.schema"."no.such.text.search.dictionary"
+\dFp "no.such.database"."no.such.schema"."no.such.text.search.parser"
+\dFt "no.such.database"."no.such.schema"."no.such.text.search.template"
+\do "no.such.database"."no.such.schema"."no.such.operator"
+\dO "no.such.database"."no.such.schema"."no.such.collation"
+\dp "no.such.database"."no.such.schema"."no.such.access.privilege"
+\dP "no.such.database"."no.such.schema"."no.such.partitioned.relation"
+\dT "no.such.database"."no.such.schema"."no.such.data.type"
+\dX "no.such.database"."no.such.schema"."no.such.extended.statistics"