From 63efb00ca40d87e853da6266d536563b0caef7f6 Mon Sep 17 00:00:00 2001 From: Mahendra Singh Thalor Date: Tue, 11 Mar 2025 18:40:00 +0530 Subject: [PATCH 1/2] move common code related to connection to new the file ConnectDatabase is used by both pg_dumpall, pg_restore and pg_dump so move common code to new file. new file name: connectdb.c --- src/bin/pg_dump/Makefile | 9 +- src/bin/pg_dump/meson.build | 2 + src/bin/pg_dump/pg_backup.h | 2 +- src/bin/pg_dump/pg_backup_archiver.c | 6 +- src/bin/pg_dump/pg_backup_db.c | 75 +------ src/bin/pg_dump/pg_dump.c | 2 +- src/bin/pg_dump/pg_dumpall.c | 279 ++------------------------- 7 files changed, 32 insertions(+), 343 deletions(-) diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile index 233ad15ca75..c488ab4aecf 100644 --- a/src/bin/pg_dump/Makefile +++ b/src/bin/pg_dump/Makefile @@ -40,7 +40,8 @@ OBJS = \ pg_backup_directory.o \ pg_backup_null.o \ pg_backup_tar.o \ - pg_backup_utils.o + pg_backup_utils.o \ + connectdb.o all: pg_dump pg_restore pg_dumpall @@ -50,8 +51,8 @@ pg_dump: pg_dump.o common.o pg_dump_sort.o $(OBJS) | submake-libpq submake-libpg pg_restore: pg_restore.o $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) pg_restore.o $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) -pg_dumpall: pg_dumpall.o dumputils.o filter.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils - $(CC) $(CFLAGS) pg_dumpall.o dumputils.o filter.o $(WIN32RES) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) +pg_dumpall: pg_dumpall.o dumputils.o filter.o connectdb.o pg_backup_utils.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils + $(CC) $(CFLAGS) pg_dumpall.o dumputils.o filter.o connectdb.o pg_backup_utils.o $(WIN32RES) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs $(INSTALL_PROGRAM) pg_dump$(X) '$(DESTDIR)$(bindir)'/pg_dump$(X) @@ -71,5 +72,5 @@ uninstall: rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X)) clean distclean: - rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o pg_dumpall.o + rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o pg_dumpall.o connectdb.o rm -rf tmp_check diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build index 603ba6cfbf0..d5f805fb511 100644 --- a/src/bin/pg_dump/meson.build +++ b/src/bin/pg_dump/meson.build @@ -30,6 +30,7 @@ pg_dump_sources = files( 'common.c', 'pg_dump.c', 'pg_dump_sort.c', + 'connectdb.c', ) if host_system == 'windows' @@ -49,6 +50,7 @@ bin_targets += pg_dump pg_dumpall_sources = files( 'pg_dumpall.c', + 'connectdb.c', ) if host_system == 'windows' diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index e783cc68d89..731cb2d19fb 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -291,7 +291,7 @@ typedef void (*SetupWorkerPtrType) (Archive *AH); * Main archiver interface. */ -extern void ConnectDatabase(Archive *AHX, +extern void ConnectDatabaseAhx(Archive *AHX, const ConnParams *cparams, bool isReconnect); extern void DisconnectDatabase(Archive *AHX); diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 7480e122b61..12f3f39e39b 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -413,7 +413,7 @@ RestoreArchive(Archive *AHX) AHX->minRemoteVersion = 0; AHX->maxRemoteVersion = 9999999; - ConnectDatabase(AHX, &ropt->cparams, false); + ConnectDatabaseAhx(AHX, &ropt->cparams, false); /* * If we're talking to the DB directly, don't send comments since they @@ -4430,7 +4430,7 @@ restore_toc_entries_postfork(ArchiveHandle *AH, TocEntry *pending_list) /* * Now reconnect the single parent connection. */ - ConnectDatabase((Archive *) AH, &ropt->cparams, true); + ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true); /* re-establish fixed state */ _doSetFixedOutputState(AH); @@ -5047,7 +5047,7 @@ CloneArchive(ArchiveHandle *AH) * Connect our new clone object to the database, using the same connection * parameters used for the original connection. */ - ConnectDatabase((Archive *) clone, &clone->public.ropt->cparams, true); + ConnectDatabaseAhx((Archive *) clone, &clone->public.ropt->cparams, true); /* re-establish fixed state */ if (AH->mode == archModeRead) diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 71c55d2466a..227dd963984 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -19,6 +19,7 @@ #include "common/connect.h" #include "common/string.h" +#include "connectdb.h" #include "parallel.h" #include "pg_backup_archiver.h" #include "pg_backup_db.h" @@ -86,9 +87,9 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname) * ArchiveHandle's connCancel, before closing old connection. Otherwise * an ill-timed SIGINT could try to access a dead connection. */ - AH->connection = NULL; /* dodge error check in ConnectDatabase */ + AH->connection = NULL; /* dodge error check in ConnectDatabaseAhx */ - ConnectDatabase((Archive *) AH, &ropt->cparams, true); + ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true); PQfinish(oldConn); } @@ -105,14 +106,13 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname) * username never does change, so one savedPassword is sufficient. */ void -ConnectDatabase(Archive *AHX, +ConnectDatabaseAhx(Archive *AHX, const ConnParams *cparams, bool isReconnect) { ArchiveHandle *AH = (ArchiveHandle *) AHX; trivalue prompt_password; char *password; - bool new_pass; if (AH->connection) pg_fatal("already connected to a database"); @@ -125,69 +125,10 @@ ConnectDatabase(Archive *AHX, if (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->username; - 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; - AH->connection = PQconnectdbParams(keywords, values, true); - - if (!AH->connection) - pg_fatal("could not connect to database"); - - if (PQstatus(AH->connection) == CONNECTION_BAD && - PQconnectionNeedsPassword(AH->connection) && - password == NULL && - prompt_password != TRI_NO) - { - PQfinish(AH->connection); - password = simple_prompt("Password: ", false); - new_pass = true; - } - } while (new_pass); - - /* check to see that the backend connection was successfully made */ - if (PQstatus(AH->connection) == CONNECTION_BAD) - { - if (isReconnect) - pg_fatal("reconnection failed: %s", - PQerrorMessage(AH->connection)); - else - pg_fatal("%s", - PQerrorMessage(AH->connection)); - } + AH->connection = ConnectDatabase(cparams->dbname, NULL, cparams->pghost, + cparams->pgport, cparams->username, + prompt_password, true, + progname, NULL, NULL, password, cparams->override_dbname); /* Start strict; later phases may override this. */ PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH, diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c371570501a..6bb54c1a2b4 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -934,7 +934,7 @@ main(int argc, char **argv) * Open the database using the Archiver, so it knows about it. Errors mean * death. */ - ConnectDatabase(fout, &dopt.cparams, false); + ConnectDatabaseAhx(fout, &dopt.cparams, false); setup_connection(fout, dumpencoding, dumpsnapshot, use_role); /* diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index e0867242526..e7e492afa28 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -24,11 +24,11 @@ #include "common/hashfn_unstable.h" #include "common/logging.h" #include "common/string.h" +#include "connectdb.h" #include "dumputils.h" #include "fe_utils/string_utils.h" #include "filter.h" #include "getopt_long.h" -#include "pg_backup.h" /* version string we expect back from pg_dump */ #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n" @@ -71,21 +71,15 @@ static void buildShSecLabels(PGconn *conn, const char *catalog_name, Oid objectId, const char *objtype, const char *objname, PQExpBuffer buffer); -static PGconn *connectDatabase(const char *dbname, - const char *connection_string, const char *pghost, - const char *pgport, const char *pguser, - trivalue prompt_password, bool fail_on_error); -static char *constructConnStr(const char **keywords, const char **values); -static PGresult *executeQuery(PGconn *conn, const char *query); static void executeCommand(PGconn *conn, const char *query); static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns, SimpleStringList *names); static void read_dumpall_filters(const char *filename, SimpleStringList *pattern); static char pg_dump_bin[MAXPGPATH]; -static const char *progname; +const char *progname; static PQExpBuffer pgdumpopts; -static char *connstr = ""; +static const char *connstr = ""; static bool output_clean = false; static bool skip_acls = false; static bool verbose = false; @@ -125,8 +119,6 @@ static char *filename = NULL; static SimpleStringList database_exclude_patterns = {NULL, NULL}; static SimpleStringList database_exclude_names = {NULL, NULL}; -#define exit_nicely(code) exit(code) - int main(int argc, char *argv[]) { @@ -483,19 +475,22 @@ main(int argc, char *argv[]) */ if (pgdb) { - conn = connectDatabase(pgdb, connstr, pghost, pgport, pguser, - prompt_password, false); + conn = ConnectDatabase(pgdb, connstr, pghost, pgport, pguser, + prompt_password, false, + progname, &connstr, &server_version, NULL, NULL); if (!conn) pg_fatal("could not connect to database \"%s\"", pgdb); } else { - conn = connectDatabase("postgres", connstr, pghost, pgport, pguser, - prompt_password, false); + conn = ConnectDatabase("postgres", connstr, pghost, pgport, pguser, + prompt_password, false, + progname, &connstr, &server_version, NULL, NULL); if (!conn) - conn = connectDatabase("template1", connstr, pghost, pgport, pguser, - prompt_password, true); + conn = ConnectDatabase("template1", connstr, pghost, pgport, pguser, + prompt_password, true, + progname, &connstr, &server_version, NULL, NULL); if (!conn) { @@ -1718,256 +1713,6 @@ buildShSecLabels(PGconn *conn, const char *catalog_name, Oid objectId, destroyPQExpBuffer(sql); } -/* - * Make a database connection with the given parameters. An - * interactive password prompt is automatically issued if required. - * - * If fail_on_error is false, we return NULL without printing any message - * on failure, but preserve any prompted password for the next try. - * - * On success, the global variable 'connstr' is set to a connection string - * containing the options used. - */ -static PGconn * -connectDatabase(const char *dbname, const char *connection_string, - const char *pghost, const char *pgport, const char *pguser, - trivalue prompt_password, bool fail_on_error) -{ - PGconn *conn; - bool new_pass; - const char *remoteversion_str; - int my_version; - const char **keywords = NULL; - const char **values = NULL; - PQconninfoOption *conn_opts = NULL; - static char *password = NULL; - - if (prompt_password == TRI_YES && !password) - password = simple_prompt("Password: ", false); - - /* - * Start the connection. Loop until we have a password if requested by - * backend. - */ - do - { - int argcount = 6; - PQconninfoOption *conn_opt; - char *err_msg = NULL; - int i = 0; - - free(keywords); - free(values); - PQconninfoFree(conn_opts); - - /* - * Merge the connection info inputs given in form of connection string - * and other options. Explicitly discard any dbname value in the - * connection string; otherwise, PQconnectdbParams() would interpret - * that value as being itself a connection string. - */ - if (connection_string) - { - conn_opts = PQconninfoParse(connection_string, &err_msg); - if (conn_opts == NULL) - pg_fatal("%s", err_msg); - - for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) - { - if (conn_opt->val != NULL && conn_opt->val[0] != '\0' && - strcmp(conn_opt->keyword, "dbname") != 0) - argcount++; - } - - keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); - values = pg_malloc0((argcount + 1) * sizeof(*values)); - - for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) - { - if (conn_opt->val != NULL && conn_opt->val[0] != '\0' && - strcmp(conn_opt->keyword, "dbname") != 0) - { - keywords[i] = conn_opt->keyword; - values[i] = conn_opt->val; - i++; - } - } - } - else - { - keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); - values = pg_malloc0((argcount + 1) * sizeof(*values)); - } - - if (pghost) - { - keywords[i] = "host"; - values[i] = pghost; - i++; - } - if (pgport) - { - keywords[i] = "port"; - values[i] = pgport; - i++; - } - if (pguser) - { - keywords[i] = "user"; - values[i] = pguser; - i++; - } - if (password) - { - keywords[i] = "password"; - values[i] = password; - i++; - } - if (dbname) - { - keywords[i] = "dbname"; - values[i] = dbname; - i++; - } - keywords[i] = "fallback_application_name"; - values[i] = progname; - i++; - - new_pass = false; - conn = PQconnectdbParams(keywords, values, true); - - if (!conn) - pg_fatal("could not connect to database \"%s\"", dbname); - - if (PQstatus(conn) == CONNECTION_BAD && - PQconnectionNeedsPassword(conn) && - !password && - prompt_password != TRI_NO) - { - PQfinish(conn); - 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_on_error) - pg_fatal("%s", PQerrorMessage(conn)); - else - { - PQfinish(conn); - - free(keywords); - free(values); - PQconninfoFree(conn_opts); - - return NULL; - } - } - - /* - * Ok, connected successfully. Remember the options used, in the form of a - * connection string. - */ - connstr = constructConnStr(keywords, values); - - free(keywords); - free(values); - PQconninfoFree(conn_opts); - - /* Check version */ - remoteversion_str = PQparameterStatus(conn, "server_version"); - if (!remoteversion_str) - pg_fatal("could not get server version"); - server_version = PQserverVersion(conn); - if (server_version == 0) - pg_fatal("could not parse server version \"%s\"", - remoteversion_str); - - my_version = PG_VERSION_NUM; - - /* - * We allow the server to be back to 9.2, and up to any minor release of - * our own major version. (See also version check in pg_dump.c.) - */ - if (my_version != server_version - && (server_version < 90200 || - (server_version / 100) > (my_version / 100))) - { - pg_log_error("aborting because of server version mismatch"); - pg_log_error_detail("server version: %s; %s version: %s", - remoteversion_str, progname, PG_VERSION); - exit_nicely(1); - } - - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL)); - - return conn; -} - -/* ---------- - * Construct a connection string from the given keyword/value pairs. It is - * used to pass the connection options to the pg_dump subprocess. - * - * The following parameters are excluded: - * dbname - varies in each pg_dump invocation - * password - it's not secure to pass a password on the command line - * fallback_application_name - we'll let pg_dump set it - * ---------- - */ -static char * -constructConnStr(const char **keywords, const char **values) -{ - PQExpBuffer buf = createPQExpBuffer(); - char *connstr; - int i; - bool firstkeyword = true; - - /* Construct a new connection string in key='value' format. */ - for (i = 0; keywords[i] != NULL; i++) - { - if (strcmp(keywords[i], "dbname") == 0 || - strcmp(keywords[i], "password") == 0 || - strcmp(keywords[i], "fallback_application_name") == 0) - continue; - - if (!firstkeyword) - appendPQExpBufferChar(buf, ' '); - firstkeyword = false; - appendPQExpBuffer(buf, "%s=", keywords[i]); - appendConnStrVal(buf, values[i]); - } - - connstr = pg_strdup(buf->data); - destroyPQExpBuffer(buf); - return connstr; -} - -/* - * Run a query, return the results, exit program on failure. - */ -static PGresult * -executeQuery(PGconn *conn, const char *query) -{ - PGresult *res; - - pg_log_info("executing %s", query); - - res = PQexec(conn, query); - if (!res || - PQresultStatus(res) != PGRES_TUPLES_OK) - { - pg_log_error("query failed: %s", PQerrorMessage(conn)); - pg_log_error_detail("Query was: %s", query); - PQfinish(conn); - exit_nicely(1); - } - - return res; -} - /* * As above for a SQL command (which returns nothing). */ -- 2.39.3