Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * Facilities for frontend code to connect to and disconnect from databases. 4 : * 5 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group 6 : * Portions Copyright (c) 1994, Regents of the University of California 7 : * 8 : * src/fe_utils/connect_utils.c 9 : * 10 : *------------------------------------------------------------------------- 11 : */ 12 : #include "postgres_fe.h" 13 : 14 : #include "common/connect.h" 15 : #include "common/logging.h" 16 : #include "common/string.h" 17 : #include "fe_utils/connect_utils.h" 18 : #include "fe_utils/query_utils.h" 19 : 20 : /* 21 : * Make a database connection with the given parameters. 22 : * 23 : * An interactive password prompt is automatically issued if needed and 24 : * allowed by cparams->prompt_password. 25 : * 26 : * If allow_password_reuse is true, we will try to re-use any password 27 : * given during previous calls to this routine. (Callers should not pass 28 : * allow_password_reuse=true unless reconnecting to the same host+port+user 29 : * as before, else we might create password exposure hazards.) 30 : */ 31 : PGconn * 32 670 : connectDatabase(const ConnParams *cparams, const char *progname, 33 : bool echo, bool fail_ok, bool allow_password_reuse) 34 : { 35 : PGconn *conn; 36 : bool new_pass; 37 : static char *password = NULL; 38 : 39 : /* Callers must supply at least dbname; other params can be NULL */ 40 : Assert(cparams->dbname); 41 : 42 670 : if (!allow_password_reuse && password) 43 : { 44 0 : free(password); 45 0 : password = NULL; 46 : } 47 : 48 670 : if (cparams->prompt_password == TRI_YES && password == NULL) 49 0 : password = simple_prompt("Password: ", false); 50 : 51 : /* 52 : * Start the connection. Loop until we have a password if requested by 53 : * backend. 54 : */ 55 : do 56 : { 57 : const char *keywords[8]; 58 : const char *values[8]; 59 670 : int i = 0; 60 : 61 : /* 62 : * If dbname is a connstring, its entries can override the other 63 : * values obtained from cparams; but in turn, override_dbname can 64 : * override the dbname component of it. 65 : */ 66 670 : keywords[i] = "host"; 67 670 : values[i++] = cparams->pghost; 68 670 : keywords[i] = "port"; 69 670 : values[i++] = cparams->pgport; 70 670 : keywords[i] = "user"; 71 670 : values[i++] = cparams->pguser; 72 670 : keywords[i] = "password"; 73 670 : values[i++] = password; 74 670 : keywords[i] = "dbname"; 75 670 : values[i++] = cparams->dbname; 76 670 : if (cparams->override_dbname) 77 : { 78 226 : keywords[i] = "dbname"; 79 226 : values[i++] = cparams->override_dbname; 80 : } 81 670 : keywords[i] = "fallback_application_name"; 82 670 : values[i++] = progname; 83 670 : keywords[i] = NULL; 84 670 : values[i++] = NULL; 85 : Assert(i <= lengthof(keywords)); 86 : 87 670 : new_pass = false; 88 670 : conn = PQconnectdbParams(keywords, values, true); 89 : 90 670 : if (!conn) 91 0 : pg_fatal("could not connect to database %s: out of memory", 92 : cparams->dbname); 93 : 94 : /* 95 : * No luck? Trying asking (again) for a password. 96 : */ 97 680 : if (PQstatus(conn) == CONNECTION_BAD && 98 10 : PQconnectionNeedsPassword(conn) && 99 0 : cparams->prompt_password != TRI_NO) 100 : { 101 0 : PQfinish(conn); 102 0 : free(password); 103 0 : password = simple_prompt("Password: ", false); 104 0 : new_pass = true; 105 : } 106 670 : } while (new_pass); 107 : 108 : /* check to see that the backend connection was successfully made */ 109 670 : if (PQstatus(conn) == CONNECTION_BAD) 110 : { 111 10 : if (fail_ok) 112 : { 113 0 : PQfinish(conn); 114 0 : return NULL; 115 : } 116 10 : pg_fatal("%s", PQerrorMessage(conn)); 117 : } 118 : 119 : /* Start strict; callers may override this. */ 120 660 : PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); 121 : 122 660 : return conn; 123 : } 124 : 125 : /* 126 : * Try to connect to the appropriate maintenance database. 127 : * 128 : * This differs from connectDatabase only in that it has a rule for 129 : * inserting a default "dbname" if none was given (which is why cparams 130 : * is not const). Note that cparams->dbname should typically come from 131 : * a --maintenance-db command line parameter. 132 : */ 133 : PGconn * 134 246 : connectMaintenanceDatabase(ConnParams *cparams, 135 : const char *progname, bool echo) 136 : { 137 : PGconn *conn; 138 : 139 : /* If a maintenance database name was specified, just connect to it. */ 140 246 : if (cparams->dbname) 141 0 : return connectDatabase(cparams, progname, echo, false, false); 142 : 143 : /* Otherwise, try postgres first and then template1. */ 144 246 : cparams->dbname = "postgres"; 145 246 : conn = connectDatabase(cparams, progname, echo, true, false); 146 246 : if (!conn) 147 : { 148 0 : cparams->dbname = "template1"; 149 0 : conn = connectDatabase(cparams, progname, echo, false, false); 150 : } 151 246 : return conn; 152 : } 153 : 154 : /* 155 : * Disconnect the given connection, canceling any statement if one is active. 156 : */ 157 : void 158 414 : disconnectDatabase(PGconn *conn) 159 : { 160 : Assert(conn != NULL); 161 : 162 414 : if (PQtransactionStatus(conn) == PQTRANS_ACTIVE) 163 : { 164 0 : PGcancelConn *cancelConn = PQcancelCreate(conn); 165 : 166 0 : (void) PQcancelBlocking(cancelConn); 167 0 : PQcancelFinish(cancelConn); 168 : } 169 : 170 414 : PQfinish(conn); 171 414 : }