Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * connectdb.c
4 : * This is a common file connection to the database.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/bin/pg_dump/connectdb.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres_fe.h"
16 :
17 : #include "common/connect.h"
18 : #include "common/logging.h"
19 : #include "common/string.h"
20 : #include "connectdb.h"
21 : #include "dumputils.h"
22 : #include "fe_utils/string_utils.h"
23 :
24 : static char *constructConnStr(const char **keywords, const char **values);
25 :
26 : /*
27 : * ConnectDatabase
28 : *
29 : * Make a database connection with the given parameters. An
30 : * interactive password prompt is automatically issued if required.
31 : *
32 : * If fail_on_error is false, we return NULL without printing any message
33 : * on failure, but preserve any prompted password for the next try.
34 : *
35 : * On success, the 'connstr' is set to a connection string containing
36 : * the options used and 'server_version' is set to version so that caller
37 : * can use them.
38 : */
39 : PGconn *
40 832 : ConnectDatabase(const char *dbname, const char *connection_string,
41 : const char *pghost, const char *pgport, const char *pguser,
42 : trivalue prompt_password, bool fail_on_error, const char *progname,
43 : const char **connstr, int *server_version, char *password,
44 : char *override_dbname)
45 : {
46 : PGconn *conn;
47 : bool new_pass;
48 : const char *remoteversion_str;
49 : int my_version;
50 832 : const char **keywords = NULL;
51 832 : const char **values = NULL;
52 832 : PQconninfoOption *conn_opts = NULL;
53 : int server_version_temp;
54 :
55 832 : if (prompt_password == TRI_YES && !password)
56 0 : password = simple_prompt("Password: ", false);
57 :
58 : /*
59 : * Start the connection. Loop until we have a password if requested by
60 : * backend.
61 : */
62 : do
63 : {
64 832 : int argcount = 8;
65 : PQconninfoOption *conn_opt;
66 832 : char *err_msg = NULL;
67 832 : int i = 0;
68 :
69 832 : free(keywords);
70 832 : free(values);
71 832 : PQconninfoFree(conn_opts);
72 :
73 : /*
74 : * Merge the connection info inputs given in form of connection string
75 : * and other options. Explicitly discard any dbname value in the
76 : * connection string; otherwise, PQconnectdbParams() would interpret
77 : * that value as being itself a connection string.
78 : */
79 832 : if (connection_string)
80 : {
81 80 : conn_opts = PQconninfoParse(connection_string, &err_msg);
82 80 : if (conn_opts == NULL)
83 0 : pg_fatal("%s", err_msg);
84 :
85 4080 : for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
86 : {
87 4000 : if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
88 50 : strcmp(conn_opt->keyword, "dbname") != 0)
89 32 : argcount++;
90 : }
91 :
92 80 : keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
93 80 : values = pg_malloc0((argcount + 1) * sizeof(*values));
94 :
95 4080 : for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
96 : {
97 4000 : if (conn_opt->val != NULL && conn_opt->val[0] != '\0' &&
98 50 : strcmp(conn_opt->keyword, "dbname") != 0)
99 : {
100 32 : keywords[i] = conn_opt->keyword;
101 32 : values[i] = conn_opt->val;
102 32 : i++;
103 : }
104 : }
105 : }
106 : else
107 : {
108 752 : keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
109 752 : values = pg_malloc0((argcount + 1) * sizeof(*values));
110 : }
111 :
112 832 : if (pghost)
113 : {
114 236 : keywords[i] = "host";
115 236 : values[i] = pghost;
116 236 : i++;
117 : }
118 832 : if (pgport)
119 : {
120 312 : keywords[i] = "port";
121 312 : values[i] = pgport;
122 312 : i++;
123 : }
124 832 : if (pguser)
125 : {
126 262 : keywords[i] = "user";
127 262 : values[i] = pguser;
128 262 : i++;
129 : }
130 832 : if (password)
131 : {
132 0 : keywords[i] = "password";
133 0 : values[i] = password;
134 0 : i++;
135 : }
136 832 : if (dbname)
137 : {
138 804 : keywords[i] = "dbname";
139 804 : values[i] = dbname;
140 804 : i++;
141 : }
142 832 : if (override_dbname)
143 : {
144 114 : keywords[i] = "dbname";
145 114 : values[i] = override_dbname;
146 114 : i++;
147 : }
148 :
149 832 : keywords[i] = "fallback_application_name";
150 832 : values[i] = progname;
151 832 : i++;
152 :
153 832 : new_pass = false;
154 832 : conn = PQconnectdbParams(keywords, values, true);
155 :
156 832 : if (!conn)
157 0 : pg_fatal("could not connect to database \"%s\"", dbname);
158 :
159 838 : if (PQstatus(conn) == CONNECTION_BAD &&
160 6 : PQconnectionNeedsPassword(conn) &&
161 0 : !password &&
162 : prompt_password != TRI_NO)
163 : {
164 0 : PQfinish(conn);
165 0 : password = simple_prompt("Password: ", false);
166 0 : new_pass = true;
167 : }
168 832 : } while (new_pass);
169 :
170 : /* check to see that the backend connection was successfully made */
171 832 : if (PQstatus(conn) == CONNECTION_BAD)
172 : {
173 6 : if (fail_on_error)
174 4 : pg_fatal("%s", PQerrorMessage(conn));
175 : else
176 : {
177 2 : PQfinish(conn);
178 :
179 2 : free(keywords);
180 2 : free(values);
181 2 : PQconninfoFree(conn_opts);
182 :
183 2 : return NULL;
184 : }
185 : }
186 :
187 : /*
188 : * Ok, connected successfully. If requested, remember the options used, in
189 : * the form of a connection string.
190 : */
191 826 : if (connstr)
192 80 : *connstr = constructConnStr(keywords, values);
193 :
194 826 : free(keywords);
195 826 : free(values);
196 826 : PQconninfoFree(conn_opts);
197 :
198 : /* Check version */
199 826 : remoteversion_str = PQparameterStatus(conn, "server_version");
200 826 : if (!remoteversion_str)
201 0 : pg_fatal("could not get server version");
202 :
203 826 : server_version_temp = PQserverVersion(conn);
204 826 : if (server_version_temp == 0)
205 0 : pg_fatal("could not parse server version \"%s\"",
206 : remoteversion_str);
207 :
208 : /* If requested, then copy server version to out variable. */
209 826 : if (server_version)
210 80 : *server_version = server_version_temp;
211 :
212 826 : my_version = PG_VERSION_NUM;
213 :
214 : /*
215 : * We allow the server to be back to 9.2, and up to any minor release of
216 : * our own major version. (See also version check in pg_dump.c.)
217 : */
218 826 : if (my_version != server_version_temp
219 0 : && (server_version_temp < 90200 ||
220 0 : (server_version_temp / 100) > (my_version / 100)))
221 : {
222 0 : pg_log_error("aborting because of server version mismatch");
223 0 : pg_log_error_detail("server version: %s; %s version: %s",
224 : remoteversion_str, progname, PG_VERSION);
225 0 : exit_nicely(1);
226 : }
227 :
228 826 : PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
229 :
230 826 : return conn;
231 : }
232 :
233 : /*
234 : * constructConnStr
235 : *
236 : * Construct a connection string from the given keyword/value pairs. It is
237 : * used to pass the connection options to the pg_dump subprocess.
238 : *
239 : * The following parameters are excluded:
240 : * dbname - varies in each pg_dump invocation
241 : * password - it's not secure to pass a password on the command line
242 : * fallback_application_name - we'll let pg_dump set it
243 : */
244 : static char *
245 80 : constructConnStr(const char **keywords, const char **values)
246 : {
247 80 : PQExpBuffer buf = createPQExpBuffer();
248 : char *connstr;
249 : int i;
250 80 : bool firstkeyword = true;
251 :
252 : /* Construct a new connection string in key='value' format. */
253 354 : for (i = 0; keywords[i] != NULL; i++)
254 : {
255 274 : if (strcmp(keywords[i], "dbname") == 0 ||
256 194 : strcmp(keywords[i], "password") == 0 ||
257 194 : strcmp(keywords[i], "fallback_application_name") == 0)
258 160 : continue;
259 :
260 114 : if (!firstkeyword)
261 64 : appendPQExpBufferChar(buf, ' ');
262 114 : firstkeyword = false;
263 114 : appendPQExpBuffer(buf, "%s=", keywords[i]);
264 114 : appendConnStrVal(buf, values[i]);
265 : }
266 :
267 80 : connstr = pg_strdup(buf->data);
268 80 : destroyPQExpBuffer(buf);
269 80 : return connstr;
270 : }
271 :
272 : /*
273 : * executeQuery
274 : *
275 : * Run a query, return the results, exit program on failure.
276 : */
277 : PGresult *
278 1922 : executeQuery(PGconn *conn, const char *query)
279 : {
280 : PGresult *res;
281 :
282 1922 : pg_log_info("executing %s", query);
283 :
284 1922 : res = PQexec(conn, query);
285 3844 : if (!res ||
286 1922 : PQresultStatus(res) != PGRES_TUPLES_OK)
287 : {
288 0 : pg_log_error("query failed: %s", PQerrorMessage(conn));
289 0 : pg_log_error_detail("Query was: %s", query);
290 0 : PQfinish(conn);
291 0 : exit_nicely(1);
292 : }
293 :
294 1922 : return res;
295 : }
|