diff options
Diffstat (limited to 'src/bin/scripts/common.c')
-rw-r--r-- | src/bin/scripts/common.c | 318 |
1 files changed, 1 insertions, 317 deletions
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", |