diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index a02e4e430c8..b8d7cf2f2d8 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -28,8 +28,8 @@ createuser: createuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport dropdb: dropdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils dropuser: dropuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils clusterdb: clusterdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils -vacuumdb: vacuumdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils -reindexdb: reindexdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils +vacuumdb: vacuumdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils +reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs @@ -50,7 +50,7 @@ uninstall: clean distclean maintainer-clean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) - rm -f common.o scripts_parallel.o $(WIN32RES) + rm -f common.o $(WIN32RES) rm -rf tmp_check check: diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c index 7d25bb31d4e..fc771eed77b 100644 --- a/src/bin/scripts/clusterdb.c +++ b/src/bin/scripts/clusterdb.c @@ -13,6 +13,8 @@ #include "common.h" #include "common/logging.h" #include "fe_utils/cancel.h" +#include "fe_utils/option_utils.h" +#include "fe_utils/query_utils.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index 21ef297e6eb..c86c19eae28 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -22,325 +22,9 @@ #include "common/logging.h" #include "common/string.h" #include "fe_utils/cancel.h" +#include "fe_utils/query_utils.h" #include "fe_utils/string_utils.h" -#define ERRCODE_UNDEFINED_TABLE "42P01" - -/* - * Provide strictly harmonized handling of --help and --version - * options. - */ -void -handle_help_version_opts(int argc, char *argv[], - const char *fixed_progname, help_handler hlp) -{ - if (argc > 1) - { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - hlp(get_progname(argv[0])); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname); - exit(0); - } - } -} - - -/* - * Make a database connection with the given parameters. - * - * An interactive password prompt is automatically issued if needed and - * allowed by cparams->prompt_password. - * - * If allow_password_reuse is true, we will try to re-use any password - * given during previous calls to this routine. (Callers should not pass - * allow_password_reuse=true unless reconnecting to the same database+user - * as before, else we might create password exposure hazards.) - */ -PGconn * -connectDatabase(const ConnParams *cparams, const char *progname, - bool echo, bool fail_ok, bool allow_password_reuse) -{ - PGconn *conn; - bool new_pass; - static char *password = NULL; - - /* Callers must supply at least dbname; other params can be NULL */ - Assert(cparams->dbname); - - if (!allow_password_reuse && password) - { - free(password); - password = NULL; - } - - if (cparams->prompt_password == TRI_YES && password == NULL) - password = simple_prompt("Password: ", false); - - /* - * Start the connection. Loop until we have a password if requested by - * backend. - */ - do - { - const char *keywords[8]; - const char *values[8]; - int i = 0; - - /* - * If dbname is a connstring, its entries can override the other - * values obtained from cparams; but in turn, override_dbname can - * override the dbname component of it. - */ - keywords[i] = "host"; - values[i++] = cparams->pghost; - keywords[i] = "port"; - values[i++] = cparams->pgport; - keywords[i] = "user"; - values[i++] = cparams->pguser; - keywords[i] = "password"; - values[i++] = password; - keywords[i] = "dbname"; - values[i++] = cparams->dbname; - if (cparams->override_dbname) - { - keywords[i] = "dbname"; - values[i++] = cparams->override_dbname; - } - keywords[i] = "fallback_application_name"; - values[i++] = progname; - keywords[i] = NULL; - values[i++] = NULL; - Assert(i <= lengthof(keywords)); - - new_pass = false; - conn = PQconnectdbParams(keywords, values, true); - - if (!conn) - { - pg_log_error("could not connect to database %s: out of memory", - cparams->dbname); - exit(1); - } - - /* - * No luck? Trying asking (again) for a password. - */ - if (PQstatus(conn) == CONNECTION_BAD && - PQconnectionNeedsPassword(conn) && - cparams->prompt_password != TRI_NO) - { - PQfinish(conn); - if (password) - free(password); - password = simple_prompt("Password: ", false); - new_pass = true; - } - } while (new_pass); - - /* check to see that the backend connection was successfully made */ - if (PQstatus(conn) == CONNECTION_BAD) - { - if (fail_ok) - { - PQfinish(conn); - return NULL; - } - pg_log_error("%s", PQerrorMessage(conn)); - exit(1); - } - - /* Start strict; callers may override this. */ - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); - - return conn; -} - -/* - * Try to connect to the appropriate maintenance database. - * - * This differs from connectDatabase only in that it has a rule for - * inserting a default "dbname" if none was given (which is why cparams - * is not const). Note that cparams->dbname should typically come from - * a --maintenance-db command line parameter. - */ -PGconn * -connectMaintenanceDatabase(ConnParams *cparams, - const char *progname, bool echo) -{ - PGconn *conn; - - /* If a maintenance database name was specified, just connect to it. */ - if (cparams->dbname) - return connectDatabase(cparams, progname, echo, false, false); - - /* Otherwise, try postgres first and then template1. */ - cparams->dbname = "postgres"; - conn = connectDatabase(cparams, progname, echo, true, false); - if (!conn) - { - cparams->dbname = "template1"; - conn = connectDatabase(cparams, progname, echo, false, false); - } - return conn; -} - -/* - * Disconnect the given connection, canceling any statement if one is active. - */ -void -disconnectDatabase(PGconn *conn) -{ - char errbuf[256]; - - Assert(conn != NULL); - - if (PQtransactionStatus(conn) == PQTRANS_ACTIVE) - { - PGcancel *cancel; - - if ((cancel = PQgetCancel(conn))) - { - (void) PQcancel(cancel, errbuf, sizeof(errbuf)); - PQfreeCancel(cancel); - } - } - - PQfinish(conn); -} - -/* - * Run a query, return the results, exit program on failure. - */ -PGresult * -executeQuery(PGconn *conn, const char *query, bool echo) -{ - PGresult *res; - - if (echo) - printf("%s\n", query); - - res = PQexec(conn, query); - if (!res || - PQresultStatus(res) != PGRES_TUPLES_OK) - { - pg_log_error("query failed: %s", PQerrorMessage(conn)); - pg_log_info("query was: %s", query); - PQfinish(conn); - exit(1); - } - - return res; -} - - -/* - * As above for a SQL command (which returns nothing). - */ -void -executeCommand(PGconn *conn, const char *query, bool echo) -{ - PGresult *res; - - if (echo) - printf("%s\n", query); - - res = PQexec(conn, query); - if (!res || - PQresultStatus(res) != PGRES_COMMAND_OK) - { - pg_log_error("query failed: %s", PQerrorMessage(conn)); - pg_log_info("query was: %s", query); - PQfinish(conn); - exit(1); - } - - PQclear(res); -} - - -/* - * As above for a SQL maintenance command (returns command success). - * Command is executed with a cancel handler set, so Ctrl-C can - * interrupt it. - */ -bool -executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) -{ - PGresult *res; - bool r; - - if (echo) - printf("%s\n", query); - - SetCancelConn(conn); - res = PQexec(conn, query); - ResetCancelConn(); - - r = (res && PQresultStatus(res) == PGRES_COMMAND_OK); - - if (res) - PQclear(res); - - return r; -} - -/* - * Consume all the results generated for the given connection until - * nothing remains. If at least one error is encountered, return false. - * Note that this will block if the connection is busy. - */ -bool -consumeQueryResult(PGconn *conn) -{ - bool ok = true; - PGresult *result; - - SetCancelConn(conn); - while ((result = PQgetResult(conn)) != NULL) - { - if (!processQueryResult(conn, result)) - ok = false; - } - ResetCancelConn(); - return ok; -} - -/* - * Process (and delete) a query result. Returns true if there's no error, - * false otherwise -- but errors about trying to work on a missing relation - * are reported and subsequently ignored. - */ -bool -processQueryResult(PGconn *conn, PGresult *result) -{ - /* - * If it's an error, report it. Errors about a missing table are harmless - * so we continue processing; but die for other errors. - */ - if (PQresultStatus(result) != PGRES_COMMAND_OK) - { - char *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE); - - pg_log_error("processing of database \"%s\" failed: %s", - PQdb(conn), PQerrorMessage(conn)); - - if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0) - { - PQclear(result); - return false; - } - } - - PQclear(result); - return true; -} - - /* * Split TABLE[(COLUMNS)] into TABLE and [(COLUMNS)] portions. When you * finish using them, pg_free(*table). *columns is a pointer into "spec", diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h index 56309757124..ddd8f352749 100644 --- a/src/bin/scripts/common.h +++ b/src/bin/scripts/common.h @@ -10,58 +10,11 @@ #define COMMON_H #include "common/username.h" +#include "fe_utils/connect_utils.h" #include "getopt_long.h" /* pgrminclude ignore */ #include "libpq-fe.h" #include "pqexpbuffer.h" /* pgrminclude ignore */ -enum trivalue -{ - TRI_DEFAULT, - TRI_NO, - TRI_YES -}; - -/* Parameters needed by connectDatabase/connectMaintenanceDatabase */ -typedef struct _connParams -{ - /* These fields record the actual command line parameters */ - const char *dbname; /* this may be a connstring! */ - const char *pghost; - const char *pgport; - const char *pguser; - enum trivalue prompt_password; - /* If not NULL, this overrides the dbname obtained from command line */ - /* (but *only* the DB name, not anything else in the connstring) */ - const char *override_dbname; -} ConnParams; - -typedef void (*help_handler) (const char *progname); - -extern void handle_help_version_opts(int argc, char *argv[], - const char *fixed_progname, - help_handler hlp); - -extern PGconn *connectDatabase(const ConnParams *cparams, - const char *progname, - bool echo, bool fail_ok, - bool allow_password_reuse); - -extern PGconn *connectMaintenanceDatabase(ConnParams *cparams, - const char *progname, bool echo); - -extern void disconnectDatabase(PGconn *conn); - -extern PGresult *executeQuery(PGconn *conn, const char *query, bool echo); - -extern void executeCommand(PGconn *conn, const char *query, bool echo); - -extern bool executeMaintenanceCommand(PGconn *conn, const char *query, - bool echo); - -extern bool consumeQueryResult(PGconn *conn); - -extern bool processQueryResult(PGconn *conn, PGresult *result); - extern void splitTableColumnsSpec(const char *spec, int encoding, char **table, const char **columns); diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c index abf21d49428..041454f075f 100644 --- a/src/bin/scripts/createdb.c +++ b/src/bin/scripts/createdb.c @@ -13,6 +13,7 @@ #include "common.h" #include "common/logging.h" +#include "fe_utils/option_utils.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c index 47b0e28bc67..ef7e0e549fb 100644 --- a/src/bin/scripts/createuser.c +++ b/src/bin/scripts/createuser.c @@ -14,6 +14,7 @@ #include "common.h" #include "common/logging.h" #include "common/string.h" +#include "fe_utils/option_utils.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c index ba0dcdecb95..b154ed1bb6d 100644 --- a/src/bin/scripts/dropdb.c +++ b/src/bin/scripts/dropdb.c @@ -13,6 +13,7 @@ #include "postgres_fe.h" #include "common.h" #include "common/logging.h" +#include "fe_utils/option_utils.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c index ff5b455ae51..61b8557bc7e 100644 --- a/src/bin/scripts/dropuser.c +++ b/src/bin/scripts/dropuser.c @@ -14,6 +14,7 @@ #include "common.h" #include "common/logging.h" #include "common/string.h" +#include "fe_utils/option_utils.h" #include "fe_utils/string_utils.h" diff --git a/src/bin/scripts/nls.mk b/src/bin/scripts/nls.mk index 5d5dd11b7be..7fc716092e7 100644 --- a/src/bin/scripts/nls.mk +++ b/src/bin/scripts/nls.mk @@ -7,7 +7,7 @@ GETTEXT_FILES = $(FRONTEND_COMMON_GETTEXT_FILES) \ clusterdb.c vacuumdb.c reindexdb.c \ pg_isready.c \ common.c \ - scripts_parallel.c \ + ../../fe_utils/parallel_slot.c \ ../../fe_utils/cancel.c ../../fe_utils/print.c \ ../../common/fe_memutils.c ../../common/username.c GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) simple_prompt yesno_prompt diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c index ceb8a09b4c5..fc6f7b0a939 100644 --- a/src/bin/scripts/pg_isready.c +++ b/src/bin/scripts/pg_isready.c @@ -12,6 +12,7 @@ #include "postgres_fe.h" #include "common.h" #include "common/logging.h" +#include "fe_utils/option_utils.h" #define DEFAULT_CONNECT_TIMEOUT "3" diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index dece8200fa2..7781fb1151a 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -16,9 +16,11 @@ #include "common/connect.h" #include "common/logging.h" #include "fe_utils/cancel.h" +#include "fe_utils/option_utils.h" +#include "fe_utils/parallel_slot.h" +#include "fe_utils/query_utils.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" -#include "scripts_parallel.h" typedef enum ReindexType { diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 82463277702..ed320817bc4 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -18,9 +18,11 @@ #include "common/connect.h" #include "common/logging.h" #include "fe_utils/cancel.h" +#include "fe_utils/option_utils.h" +#include "fe_utils/parallel_slot.h" +#include "fe_utils/query_utils.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" -#include "scripts_parallel.h" /* vacuum options controlled by user flags */ diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile index 10d6838cf96..456c441a334 100644 --- a/src/fe_utils/Makefile +++ b/src/fe_utils/Makefile @@ -23,9 +23,13 @@ OBJS = \ archive.o \ cancel.o \ conditional.o \ + connect_utils.o \ mbprint.o \ + option_utils.o \ + parallel_slot.o \ print.o \ psqlscan.o \ + query_utils.o \ recovery_gen.o \ simple_list.o \ string_utils.o diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c new file mode 100644 index 00000000000..96bb798316a --- /dev/null +++ b/src/fe_utils/connect_utils.c @@ -0,0 +1,181 @@ +/*------------------------------------------------------------------------- + * + * Facilities for frontend code to connect to and disconnect from databases. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/connect_utils.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "common/connect.h" +#include "common/logging.h" +#include "common/string.h" +#include "fe_utils/connect_utils.h" +#include "fe_utils/query_utils.h" + +/* + * Make a database connection with the given parameters. + * + * An interactive password prompt is automatically issued if needed and + * allowed by cparams->prompt_password. + * + * If allow_password_reuse is true, we will try to re-use any password + * given during previous calls to this routine. (Callers should not pass + * allow_password_reuse=true unless reconnecting to the same database+user + * as before, else we might create password exposure hazards.) + */ +PGconn * +connectDatabase(const ConnParams *cparams, const char *progname, + bool echo, bool fail_ok, bool allow_password_reuse) +{ + PGconn *conn; + bool new_pass; + static char *password = NULL; + + /* Callers must supply at least dbname; other params can be NULL */ + Assert(cparams->dbname); + + if (!allow_password_reuse && password) + { + free(password); + password = NULL; + } + + if (cparams->prompt_password == TRI_YES && password == NULL) + password = simple_prompt("Password: ", false); + + /* + * Start the connection. Loop until we have a password if requested by + * backend. + */ + do + { + const char *keywords[8]; + const char *values[8]; + int i = 0; + + /* + * If dbname is a connstring, its entries can override the other + * values obtained from cparams; but in turn, override_dbname can + * override the dbname component of it. + */ + keywords[i] = "host"; + values[i++] = cparams->pghost; + keywords[i] = "port"; + values[i++] = cparams->pgport; + keywords[i] = "user"; + values[i++] = cparams->pguser; + keywords[i] = "password"; + values[i++] = password; + keywords[i] = "dbname"; + values[i++] = cparams->dbname; + if (cparams->override_dbname) + { + keywords[i] = "dbname"; + values[i++] = cparams->override_dbname; + } + keywords[i] = "fallback_application_name"; + values[i++] = progname; + keywords[i] = NULL; + values[i++] = NULL; + Assert(i <= lengthof(keywords)); + + new_pass = false; + conn = PQconnectdbParams(keywords, values, true); + + if (!conn) + { + pg_log_error("could not connect to database %s: out of memory", + cparams->dbname); + exit(1); + } + + /* + * No luck? Trying asking (again) for a password. + */ + if (PQstatus(conn) == CONNECTION_BAD && + PQconnectionNeedsPassword(conn) && + cparams->prompt_password != TRI_NO) + { + PQfinish(conn); + if (password) + free(password); + password = simple_prompt("Password: ", false); + new_pass = true; + } + } while (new_pass); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(conn) == CONNECTION_BAD) + { + if (fail_ok) + { + PQfinish(conn); + return NULL; + } + pg_log_error("%s", PQerrorMessage(conn)); + exit(1); + } + + /* Start strict; callers may override this. */ + PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); + + return conn; +} + +/* + * Try to connect to the appropriate maintenance database. + * + * This differs from connectDatabase only in that it has a rule for + * inserting a default "dbname" if none was given (which is why cparams + * is not const). Note that cparams->dbname should typically come from + * a --maintenance-db command line parameter. + */ +PGconn * +connectMaintenanceDatabase(ConnParams *cparams, + const char *progname, bool echo) +{ + PGconn *conn; + + /* If a maintenance database name was specified, just connect to it. */ + if (cparams->dbname) + return connectDatabase(cparams, progname, echo, false, false); + + /* Otherwise, try postgres first and then template1. */ + cparams->dbname = "postgres"; + conn = connectDatabase(cparams, progname, echo, true, false); + if (!conn) + { + cparams->dbname = "template1"; + conn = connectDatabase(cparams, progname, echo, false, false); + } + return conn; +} + +/* + * Disconnect the given connection, canceling any statement if one is active. + */ +void +disconnectDatabase(PGconn *conn) +{ + char errbuf[256]; + + Assert(conn != NULL); + + if (PQtransactionStatus(conn) == PQTRANS_ACTIVE) + { + PGcancel *cancel; + + if ((cancel = PQgetCancel(conn))) + { + (void) PQcancel(cancel, errbuf, sizeof(errbuf)); + PQfreeCancel(cancel); + } + } + + PQfinish(conn); +} diff --git a/src/fe_utils/option_utils.c b/src/fe_utils/option_utils.c new file mode 100644 index 00000000000..e19a495dba7 --- /dev/null +++ b/src/fe_utils/option_utils.c @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * Command line option processing facilities for frontend code + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/option_utils.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include "fe_utils/option_utils.h" + +/* + * Provide strictly harmonized handling of --help and --version + * options. + */ +void +handle_help_version_opts(int argc, char *argv[], + const char *fixed_progname, help_handler hlp) +{ + if (argc > 1) + { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) + { + hlp(get_progname(argv[0])); + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname); + exit(0); + } + } +} diff --git a/src/bin/scripts/scripts_parallel.c b/src/fe_utils/parallel_slot.c similarity index 80% rename from src/bin/scripts/scripts_parallel.c rename to src/fe_utils/parallel_slot.c index 1f863a1bb46..3987a4702b5 100644 --- a/src/bin/scripts/scripts_parallel.c +++ b/src/fe_utils/parallel_slot.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * - * scripts_parallel.c - * Parallel support for bin/scripts/ + * parallel_slot.c + * Parallel support for front-end parallel database connections * * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * src/bin/scripts/scripts_parallel.c + * src/fe_utils/parallel_slot.c * *------------------------------------------------------------------------- */ @@ -22,13 +22,15 @@ #include #endif -#include "common.h" #include "common/logging.h" #include "fe_utils/cancel.h" -#include "scripts_parallel.h" +#include "fe_utils/parallel_slot.h" + +#define ERRCODE_UNDEFINED_TABLE "42P01" static void init_slot(ParallelSlot *slot, PGconn *conn); static int select_loop(int maxFd, fd_set *workerset); +static bool processQueryResult(PGconn *conn, PGresult *result); static void init_slot(ParallelSlot *slot, PGconn *conn) @@ -38,6 +40,57 @@ init_slot(ParallelSlot *slot, PGconn *conn) slot->isFree = true; } +/* + * Process (and delete) a query result. Returns true if there's no error, + * false otherwise -- but errors about trying to work on a missing relation + * are reported and subsequently ignored. + */ +static bool +processQueryResult(PGconn *conn, PGresult *result) +{ + /* + * If it's an error, report it. Errors about a missing table are harmless + * so we continue processing; but die for other errors. + */ + if (PQresultStatus(result) != PGRES_COMMAND_OK) + { + char *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE); + + pg_log_error("processing of database \"%s\" failed: %s", + PQdb(conn), PQerrorMessage(conn)); + + if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0) + { + PQclear(result); + return false; + } + } + + PQclear(result); + return true; +} + +/* + * Consume all the results generated for the given connection until + * nothing remains. If at least one error is encountered, return false. + * Note that this will block if the connection is busy. + */ +static bool +consumeQueryResult(PGconn *conn) +{ + bool ok = true; + PGresult *result; + + SetCancelConn(conn); + while ((result = PQgetResult(conn)) != NULL) + { + if (!processQueryResult(conn, result)) + ok = false; + } + ResetCancelConn(); + return ok; +} + /* * Wait until a file descriptor from the given set becomes readable. * diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c new file mode 100644 index 00000000000..d5ffe56fd68 --- /dev/null +++ b/src/fe_utils/query_utils.c @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------- + * + * Facilities for frontend code to query a databases. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/query_utils.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "common/logging.h" +#include "fe_utils/cancel.h" +#include "fe_utils/query_utils.h" + +/* + * Run a query, return the results, exit program on failure. + */ +PGresult * +executeQuery(PGconn *conn, const char *query, bool echo) +{ + PGresult *res; + + if (echo) + printf("%s\n", query); + + res = PQexec(conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) + { + pg_log_error("query failed: %s", PQerrorMessage(conn)); + pg_log_info("query was: %s", query); + PQfinish(conn); + exit(1); + } + + return res; +} + + +/* + * As above for a SQL command (which returns nothing). + */ +void +executeCommand(PGconn *conn, const char *query, bool echo) +{ + PGresult *res; + + if (echo) + printf("%s\n", query); + + res = PQexec(conn, query); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) + { + pg_log_error("query failed: %s", PQerrorMessage(conn)); + pg_log_info("query was: %s", query); + PQfinish(conn); + exit(1); + } + + PQclear(res); +} + + +/* + * As above for a SQL maintenance command (returns command success). + * Command is executed with a cancel handler set, so Ctrl-C can + * interrupt it. + */ +bool +executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) +{ + PGresult *res; + bool r; + + if (echo) + printf("%s\n", query); + + SetCancelConn(conn); + res = PQexec(conn, query); + ResetCancelConn(); + + r = (res && PQresultStatus(res) == PGRES_COMMAND_OK); + + if (res) + PQclear(res); + + return r; +} diff --git a/src/include/fe_utils/connect_utils.h b/src/include/fe_utils/connect_utils.h new file mode 100644 index 00000000000..50489405091 --- /dev/null +++ b/src/include/fe_utils/connect_utils.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * Facilities for frontend code to connect to and disconnect from databases. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/connect_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef CONNECT_UTILS_H +#define CONNECT_UTILS_H + +#include "libpq-fe.h" + +enum trivalue +{ + TRI_DEFAULT, + TRI_NO, + TRI_YES +}; + +/* Parameters needed by connectDatabase/connectMaintenanceDatabase */ +typedef struct _connParams +{ + /* These fields record the actual command line parameters */ + const char *dbname; /* this may be a connstring! */ + const char *pghost; + const char *pgport; + const char *pguser; + enum trivalue prompt_password; + /* If not NULL, this overrides the dbname obtained from command line */ + /* (but *only* the DB name, not anything else in the connstring) */ + const char *override_dbname; +} ConnParams; + +extern PGconn *connectDatabase(const ConnParams *cparams, + const char *progname, + bool echo, bool fail_ok, + bool allow_password_reuse); + +extern PGconn *connectMaintenanceDatabase(ConnParams *cparams, + const char *progname, bool echo); + +extern void disconnectDatabase(PGconn *conn); + +#endif /* CONNECT_UTILS_H */ diff --git a/src/include/fe_utils/option_utils.h b/src/include/fe_utils/option_utils.h new file mode 100644 index 00000000000..d653cb94e34 --- /dev/null +++ b/src/include/fe_utils/option_utils.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * Command line option processing facilities for frontend code + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/option_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef OPTION_UTILS_H +#define OPTION_UTILS_H + +#include "postgres_fe.h" + +typedef void (*help_handler) (const char *progname); + +extern void handle_help_version_opts(int argc, char *argv[], + const char *fixed_progname, + help_handler hlp); + +#endif /* OPTION_UTILS_H */ diff --git a/src/bin/scripts/scripts_parallel.h b/src/include/fe_utils/parallel_slot.h similarity index 82% rename from src/bin/scripts/scripts_parallel.h rename to src/include/fe_utils/parallel_slot.h index f62692510a8..99eeb3328d6 100644 --- a/src/bin/scripts/scripts_parallel.h +++ b/src/include/fe_utils/parallel_slot.h @@ -1,21 +1,20 @@ /*------------------------------------------------------------------------- * - * scripts_parallel.h + * parallel_slot.h * Parallel support for bin/scripts/ * * Copyright (c) 2003-2021, PostgreSQL Global Development Group * - * src/bin/scripts/scripts_parallel.h + * src/include/fe_utils/parallel_slot.h * *------------------------------------------------------------------------- */ -#ifndef SCRIPTS_PARALLEL_H -#define SCRIPTS_PARALLEL_H +#ifndef PARALLEL_SLOT_H +#define PARALLEL_SLOT_H -#include "common.h" +#include "fe_utils/connect_utils.h" #include "libpq-fe.h" - typedef struct ParallelSlot { PGconn *connection; /* One connection */ @@ -33,4 +32,4 @@ extern void ParallelSlotsTerminate(ParallelSlot *slots, int numslots); extern bool ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots); -#endif /* SCRIPTS_PARALLEL_H */ +#endif /* PARALLEL_SLOT_H */ diff --git a/src/include/fe_utils/query_utils.h b/src/include/fe_utils/query_utils.h new file mode 100644 index 00000000000..10992601932 --- /dev/null +++ b/src/include/fe_utils/query_utils.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * Facilities for frontend code to query a databases. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/query_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef QUERY_UTILS_H +#define QUERY_UTILS_H + +#include "postgres_fe.h" + +#include "libpq-fe.h" + +extern PGresult *executeQuery(PGconn *conn, const char *query, bool echo); + +extern void executeCommand(PGconn *conn, const char *query, bool echo); + +extern bool executeMaintenanceCommand(PGconn *conn, const char *query, + bool echo); + +#endif /* QUERY_UTILS_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 90328db04e9..49614106dc2 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -147,8 +147,9 @@ sub mkvcbuild our @pgcommonbkndfiles = @pgcommonallfiles; our @pgfeutilsfiles = qw( - archive.c cancel.c conditional.c mbprint.c print.c psqlscan.l - psqlscan.c simple_list.c string_utils.c recovery_gen.c); + archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c + parallel_slot.c print.c psqlscan.l psqlscan.c query_utils.c simple_list.c + string_utils.c recovery_gen.c); $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); $libpgport->AddDefine('FRONTEND');