From 30ec41aa015af53af03d13f621f04f5a2ae1c20b Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Sun, 17 Jan 2021 11:35:57 -0800 Subject: [PATCH v33 2/8] Introducing PGresultHandler abstraction Many frontend applications in src/bin and src/bin/scripts have functions that include the pattern: result = PQexec(...) if (PQresultStatus(result) != ...) { pg_log_{notice,warning,error} ... ... exit(1) } The exact error handling behavior differs from application to application. Factoring functions out of frontend applications into src/fe_utils requires abstracting the error handling so that various callers can specify different behavior consistent with their idiosyncratic needs. For callers without idiosyncratic needs, who simply want to do whatever is normal and customary, a default handler is defined. This seems cleaner than duplicating the implementation of the most customary error handling behavior across numerous frontend applications. It also makes it easier for a code contributer to know which behavior is standard, given that they would otherwise need to survey all frontend applications trying to deduce which behavior is most common. Not done here, but the src/bin/scripts/scripts_parallel.[ch] have hardcoded error handling appropriate to vacuumdb.c and reindexdb.c, including the assumption that the parallel slots implement a command rather than a query, and that no rows will be returned. The PGresultHandler abstraction will be used to make parallel slots more flexible so that pg_amcheck (which executes queries and expects rows to be returned) can use them also. --- src/fe_utils/Makefile | 1 + src/fe_utils/pgreshandler.c | 55 +++++++++++++++++++++++++++++ src/include/fe_utils/pgreshandler.h | 27 ++++++++++++++ src/tools/msvc/Mkvcbuild.pm | 2 +- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/fe_utils/pgreshandler.c create mode 100644 src/include/fe_utils/pgreshandler.h diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile index d6c328faf1..bc77d42dbe 100644 --- a/src/fe_utils/Makefile +++ b/src/fe_utils/Makefile @@ -25,6 +25,7 @@ OBJS = \ conditional.o \ exit_utils.o \ mbprint.o \ + pgreshandler.o \ print.o \ psqlscan.o \ recovery_gen.o \ diff --git a/src/fe_utils/pgreshandler.c b/src/fe_utils/pgreshandler.c new file mode 100644 index 0000000000..689c6770a5 --- /dev/null +++ b/src/fe_utils/pgreshandler.c @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * Supporting routines for modular handling of PGresult error conditions. + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/pgreshandler.c + * + *------------------------------------------------------------------------- + */ + +#include "fe_utils/exit_utils.h" +#include "fe_utils/pgreshandler.h" + +/* + * Implements the PGresultHandler abstract interface. + * + * The pgres_default_handler can be used by applications not requiring + * divergent query error handling functionality. It checks a PGresult against + * the supplied expectations for status and number of rows returned. If they + * do not match, it logs errors and exits. The exact error messages and log + * levels are chosen to precisely match the historical pg_dump behavior. + */ +PGresult * +pgres_default_handler(PGresult *res, PGconn *conn, + ExecStatusType expected_status, int expected_ntups, + const char *query) +{ + if (PQresultStatus(res) != expected_status) + { + pg_log_error("query failed: %s", PQerrorMessage(conn)); + pg_log_error("query was: %s", query); + PQfinish(conn); + exit_nicely(1); + } + if (expected_ntups >= 0) + { + int ntups = PQntuples(res); + + if (ntups != expected_ntups) + { + if (expected_ntups == 1) + fatal(ngettext("query returned %d row instead of one: %s", + "query returned %d rows instead of one: %s", + ntups), + ntups, query); + fatal(ngettext("query returned %d row instead of %d: %s", + "query returned %d rows instead of %d: %s", + ntups), + ntups, expected_ntups, query); + } + } + return res; +} diff --git a/src/include/fe_utils/pgreshandler.h b/src/include/fe_utils/pgreshandler.h new file mode 100644 index 0000000000..4cc4c02eca --- /dev/null +++ b/src/include/fe_utils/pgreshandler.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * Modular handling of PGresult error conditions. + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/query_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef PGRES_HANDLER_H +#define PGRES_HANDLER_H + +#include "libpq-fe.h" + +typedef PGresult *(*PGresultHandler) (PGresult *res, + PGconn *conn, + ExecStatusType expected_status, + int expected_ntups, + const char *query); + +extern PGresult *pgres_default_handler(PGresult *res, PGconn *conn, + ExecStatusType expected_status, + int expected_ntups, const char *query); + +#endif /* PGRES_HANDLER_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 0c6c1c996f..4385962c7c 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -147,7 +147,7 @@ sub mkvcbuild our @pgcommonbkndfiles = @pgcommonallfiles; our @pgfeutilsfiles = qw( - archive.c cancel.c conditional.c exit_utils.c mbprint.c print.c psqlscan.l + archive.c cancel.c conditional.c exit_utils.c mbprint.c pgreshandler.c print.c psqlscan.l psqlscan.c simple_list.c string_utils.c recovery_gen.c); $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); -- 2.21.1 (Apple Git-122.3)