From 86ea629bc4ede9c51f9c000caddf1969b593b569 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Sat, 12 Apr 2025 22:30:17 -0500 Subject: [PATCH v3] Print empty table when a given object is not found in a slash command Currently, in a few cases we return an error of the form Did not find any XXXX named YYYY while in some cases we print a table with no rows. This patch changes a few of the former to the later so that we have an uniform interface. --- src/bin/psql/describe.c | 377 ++++++++++++++++++++++-------------- src/bin/psql/t/001_basic.pl | 26 +++ 2 files changed, 258 insertions(+), 145 deletions(-) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 36f24502842..de1bb31b231 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -136,6 +136,12 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -210,6 +216,12 @@ describeAccessMethods(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -272,6 +284,12 @@ describeTablespaces(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -621,6 +639,12 @@ describeFunctions(const char *functypes, const char *func_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (func_pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -727,6 +751,12 @@ describeTypes(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -928,6 +958,12 @@ describeOperators(const char *oper_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (oper_pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -1037,6 +1073,12 @@ listAllDbs(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -1270,6 +1312,13 @@ listDefaultACLs(const char *pattern) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + termPQExpBuffer(&buf); + PQclear(res); + return false; + } + termPQExpBuffer(&buf); PQclear(res); return true; @@ -1522,16 +1571,14 @@ describeTableDetails(const char *pattern, bool verbose, bool showSystem) if (PQntuples(res) == 0) { - if (!pset.quiet) - { - if (pattern) - pg_log_error("Did not find any relation named \"%s\".", - pattern); - else - pg_log_error("Did not find any relations."); - } PQclear(res); - return false; + /* + * Print an empty table and return failure when no objects match + */ + if (pattern) + return listTables("tvmsE", pattern, verbose, showSystem); + else + return true; } for (i = 0; i < PQntuples(res); i++) @@ -1720,14 +1767,6 @@ describeOneTableDetails(const char *schemaname, if (!res) goto error_return; - /* Did we get anything? */ - if (PQntuples(res) == 0) - { - if (!pset.quiet) - pg_log_error("Did not find any relation with OID %s.", oid); - goto error_return; - } - tableinfo.checks = atoi(PQgetvalue(res, 0, 0)); tableinfo.relkind = *(PQgetvalue(res, 0, 1)); tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0; @@ -3797,6 +3836,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem) return false; nrows = PQntuples(res); + attr = pg_malloc0((nrows + 1) * sizeof(*attr)); printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows); @@ -3873,6 +3913,12 @@ describeRoles(const char *pattern, bool verbose, bool showSystem) free(attr[i]); free(attr); + if (pattern && nrows == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -3921,29 +3967,15 @@ listDbRoleSettings(const char *pattern, const char *pattern2) if (!res) return false; - /* - * Most functions in this file are content to print an empty table when - * there are no matching objects. We intentionally deviate from that - * here, but only in !quiet mode, because of the possibility that the user - * is confused about what the two pattern arguments mean. - */ - if (PQntuples(res) == 0 && !pset.quiet) - { - if (pattern && pattern2) - pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".", - pattern, pattern2); - else if (pattern) - pg_log_error("Did not find any settings for role \"%s\".", - pattern); - else - pg_log_error("Did not find any settings."); - } - else - { - myopt.title = _("List of settings"); - myopt.translate_header = true; + myopt.title = _("List of settings"); + myopt.translate_header = true; - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + if ((pattern || pattern2) && PQntuples(res) == 0) + { + PQclear(res); + return false; } PQclear(res); @@ -4018,6 +4050,12 @@ describeRoleGrants(const char *pattern, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4201,76 +4239,25 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys if (!res) return false; - /* - * Most functions in this file are content to print an empty table when - * there are no matching objects. We intentionally deviate from that - * here, but only in !quiet mode, for historical reasons. - */ - if (PQntuples(res) == 0 && !pset.quiet) - { - if (pattern) - { - if (ntypes != 1) - pg_log_error("Did not find any relations named \"%s\".", - pattern); - else if (showTables) - pg_log_error("Did not find any tables named \"%s\".", - pattern); - else if (showIndexes) - pg_log_error("Did not find any indexes named \"%s\".", - pattern); - else if (showViews) - pg_log_error("Did not find any views named \"%s\".", - pattern); - else if (showMatViews) - pg_log_error("Did not find any materialized views named \"%s\".", - pattern); - else if (showSeq) - pg_log_error("Did not find any sequences named \"%s\".", - pattern); - else if (showForeign) - pg_log_error("Did not find any foreign tables named \"%s\".", - pattern); - else /* should not get here */ - pg_log_error_internal("Did not find any ??? named \"%s\".", - pattern); - } - else - { - if (ntypes != 1) - pg_log_error("Did not find any relations."); - else if (showTables) - pg_log_error("Did not find any tables."); - else if (showIndexes) - pg_log_error("Did not find any indexes."); - else if (showViews) - pg_log_error("Did not find any views."); - else if (showMatViews) - pg_log_error("Did not find any materialized views."); - else if (showSeq) - pg_log_error("Did not find any sequences."); - else if (showForeign) - pg_log_error("Did not find any foreign tables."); - else /* should not get here */ - pg_log_error_internal("Did not find any ??? relations."); - } - } - else - { - myopt.title = - (ntypes != 1) ? _("List of relations") : - (showTables) ? _("List of tables") : - (showIndexes) ? _("List of indexes") : - (showViews) ? _("List of views") : - (showMatViews) ? _("List of materialized views") : - (showSeq) ? _("List of sequences") : - (showForeign) ? _("List of foreign tables") : - "List of ???"; /* should not get here */ - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); + myopt.title = + (ntypes != 1) ? _("List of relations") : + (showTables) ? _("List of tables") : + (showIndexes) ? _("List of indexes") : + (showViews) ? _("List of views") : + (showMatViews) ? _("List of materialized views") : + (showSeq) ? _("List of sequences") : + (showForeign) ? _("List of foreign tables") : + "List of ???"; /* should not get here */ + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; } PQclear(res); @@ -4568,6 +4555,12 @@ listLanguages(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4652,6 +4645,12 @@ listDomains(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4732,6 +4731,12 @@ listConversions(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4800,6 +4805,12 @@ describeConfigurationParameters(const char *pattern, bool verbose, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4880,6 +4891,12 @@ listEventTriggers(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -4976,6 +4993,12 @@ listExtendedStats(const char *pattern) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -5223,6 +5246,12 @@ listCollations(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -5327,6 +5356,13 @@ listSchemas(const char *pattern, bool verbose, bool showSystem) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + termPQExpBuffer(&buf); + PQclear(res); + return false; + } + termPQExpBuffer(&buf); PQclear(res); @@ -5398,6 +5434,12 @@ listTSParsers(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -5440,16 +5482,11 @@ listTSParsersVerbose(const char *pattern) if (PQntuples(res) == 0) { - if (!pset.quiet) - { - if (pattern) - pg_log_error("Did not find any text search parser named \"%s\".", - pattern); - else - pg_log_error("Did not find any text search parsers."); - } PQclear(res); - return false; + if (pattern) + return listTSParsers(pattern, false); + else + return true; } for (i = 0; i < PQntuples(res); i++) @@ -5775,6 +5812,12 @@ listTSConfigs(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -5818,16 +5861,11 @@ listTSConfigsVerbose(const char *pattern) if (PQntuples(res) == 0) { - if (!pset.quiet) - { - if (pattern) - pg_log_error("Did not find any text search configuration named \"%s\".", - pattern); - else - pg_log_error("Did not find any text search configurations."); - } PQclear(res); - return false; + if (pattern) + return listTSConfigs(pattern, false); + else + return true; } for (i = 0; i < PQntuples(res); i++) @@ -5996,6 +6034,12 @@ listForeignDataWrappers(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -6072,6 +6116,12 @@ listForeignServers(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -6127,6 +6177,12 @@ listUserMappings(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -6199,6 +6255,12 @@ listForeignTables(const char *pattern, bool verbose) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -6253,6 +6315,12 @@ listExtensions(const char *pattern) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; } @@ -6293,16 +6361,11 @@ listExtensionContents(const char *pattern) if (PQntuples(res) == 0) { - if (!pset.quiet) - { - if (pattern) - pg_log_error("Did not find any extension named \"%s\".", - pattern); - else - pg_log_error("Did not find any extensions."); - } PQclear(res); - return false; + if (pattern) + return listExtensions(pattern); + else + return true; } for (i = 0; i < PQntuples(res); i++) @@ -6510,6 +6573,12 @@ listPublications(const char *pattern) printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if (pattern && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -6660,18 +6729,12 @@ describePublications(const char *pattern) if (PQntuples(res) == 0) { - if (!pset.quiet) - { - if (pattern) - pg_log_error("Did not find any publication named \"%s\".", - pattern); - else - pg_log_error("Did not find any publications."); - } - termPQExpBuffer(&buf); PQclear(res); - return false; + if (pattern) + return listPublications(pattern); + else + return true; } for (i = 0; i < PQntuples(res); i++) @@ -7051,6 +7114,12 @@ listOperatorClasses(const char *access_method_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if ((access_method_pattern || type_pattern) && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -7139,6 +7208,12 @@ listOperatorFamilies(const char *access_method_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if ((access_method_pattern || type_pattern) && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -7246,6 +7321,12 @@ listOpFamilyOperators(const char *access_method_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if ((access_method_pattern || family_pattern) && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; @@ -7338,6 +7419,12 @@ listOpFamilyFunctions(const char *access_method_pattern, printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + if ((access_method_pattern || family_pattern) && PQntuples(res) == 0) + { + PQclear(res); + return false; + } + PQclear(res); return true; diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl index cf07a9dbd5e..011a6508df0 100644 --- a/src/bin/psql/t/001_basic.pl +++ b/src/bin/psql/t/001_basic.pl @@ -537,4 +537,30 @@ psql_fails_like( qr/backslash commands are restricted; only \\unrestrict is allowed/, 'meta-command in restrict mode fails'); +# Test that \d commands return exit code 1 when pattern matches nothing +# and don't print "Did not find" error messages +{ + my ($ret, $stdout, $stderr); + + ($ret, $stdout, $stderr) = $node->psql('postgres', '\\d nonexistent_table'); + isnt($ret, 0, '\\d with non-existent pattern: exit code not 0'); + unlike($stderr, qr/Did not find/, '\\d with non-existent pattern: no error message'); + + ($ret, $stdout, $stderr) = $node->psql('postgres', '\\dt nonexistent_table'); + isnt($ret, 0, '\\dt with non-existent pattern: exit code not 0'); + unlike($stderr, qr/Did not find/, '\\dt with non-existent pattern: no error message'); + + ($ret, $stdout, $stderr) = $node->psql('postgres', '\\df nonexistent_func'); + isnt($ret, 0, '\\df with non-existent pattern: exit code not 0'); + unlike($stderr, qr/Did not find/, '\\df with non-existent pattern: no error message'); + + ($ret, $stdout, $stderr) = $node->psql('postgres', '\\dx+ nonexistent_ext'); + isnt($ret, 0, '\\dx+ with non-existent pattern: exit code not 0'); + unlike($stderr, qr/Did not find/, '\\dx+ with non-existent pattern: no error message'); + + # Test that \d commands without patterns succeed even with no results + ($ret, $stdout, $stderr) = $node->psql('postgres', '\\d'); + is($ret, 0, '\\d without pattern: exit code 0'); +} + done_testing(); -- 2.50.1