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 : }
147 :
148 832 : keywords[i] = "fallback_application_name";
149 832 : values[i] = progname;
150 832 : i++;
151 :
152 832 : new_pass = false;
153 832 : conn = PQconnectdbParams(keywords, values, true);
154 :
155 832 : if (!conn)
156 0 : pg_fatal("could not connect to database \"%s\"", dbname);
157 :
158 838 : if (PQstatus(conn) == CONNECTION_BAD &&
159 6 : PQconnectionNeedsPassword(conn) &&
160 0 : !password &&
161 : prompt_password != TRI_NO)
162 : {
163 0 : PQfinish(conn);
164 0 : password = simple_prompt("Password: ", false);
165 0 : new_pass = true;
166 : }
167 832 : } while (new_pass);
168 :
169 : /* check to see that the backend connection was successfully made */
170 832 : if (PQstatus(conn) == CONNECTION_BAD)
171 : {
172 6 : if (fail_on_error)
173 4 : pg_fatal("%s", PQerrorMessage(conn));
174 : else
175 : {
176 2 : PQfinish(conn);
177 :
178 2 : free(keywords);
179 2 : free(values);
180 2 : PQconninfoFree(conn_opts);
181 :
182 2 : return NULL;
183 : }
184 : }
185 :
186 : /*
187 : * Ok, connected successfully. If requested, remember the options used, in
188 : * the form of a connection string.
189 : */
190 826 : if (connstr)
191 80 : *connstr = constructConnStr(keywords, values);
192 :
193 826 : free(keywords);
194 826 : free(values);
195 826 : PQconninfoFree(conn_opts);
196 :
197 : /* Check version */
198 826 : remoteversion_str = PQparameterStatus(conn, "server_version");
199 826 : if (!remoteversion_str)
200 0 : pg_fatal("could not get server version");
201 :
202 826 : server_version_temp = PQserverVersion(conn);
203 826 : if (server_version_temp == 0)
204 0 : pg_fatal("could not parse server version \"%s\"",
205 : remoteversion_str);
206 :
207 : /* If requested, then copy server version to out variable. */
208 826 : if (server_version)
209 80 : *server_version = server_version_temp;
210 :
211 826 : my_version = PG_VERSION_NUM;
212 :
213 : /*
214 : * We allow the server to be back to 9.2, and up to any minor release of
215 : * our own major version. (See also version check in pg_dump.c.)
216 : */
217 826 : if (my_version != server_version_temp
218 0 : && (server_version_temp < 90200 ||
219 0 : (server_version_temp / 100) > (my_version / 100)))
220 : {
221 0 : pg_log_error("aborting because of server version mismatch");
222 0 : pg_log_error_detail("server version: %s; %s version: %s",
223 : remoteversion_str, progname, PG_VERSION);
224 0 : exit_nicely(1);
225 : }
226 :
227 826 : PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
228 :
229 826 : return conn;
230 : }
231 :
232 : /*
233 : * constructConnStr
234 : *
235 : * Construct a connection string from the given keyword/value pairs. It is
236 : * used to pass the connection options to the pg_dump subprocess.
237 : *
238 : * The following parameters are excluded:
239 : * dbname - varies in each pg_dump invocation
240 : * password - it's not secure to pass a password on the command line
241 : * fallback_application_name - we'll let pg_dump set it
242 : */
243 : static char *
244 80 : constructConnStr(const char **keywords, const char **values)
245 : {
246 80 : PQExpBuffer buf = createPQExpBuffer();
247 : char *connstr;
248 : int i;
249 80 : bool firstkeyword = true;
250 :
251 : /* Construct a new connection string in key='value' format. */
252 354 : for (i = 0; keywords[i] != NULL; i++)
253 : {
254 274 : if (strcmp(keywords[i], "dbname") == 0 ||
255 194 : strcmp(keywords[i], "password") == 0 ||
256 194 : strcmp(keywords[i], "fallback_application_name") == 0)
257 160 : continue;
258 :
259 114 : if (!firstkeyword)
260 64 : appendPQExpBufferChar(buf, ' ');
261 114 : firstkeyword = false;
262 114 : appendPQExpBuffer(buf, "%s=", keywords[i]);
263 114 : appendConnStrVal(buf, values[i]);
264 : }
265 :
266 80 : connstr = pg_strdup(buf->data);
267 80 : destroyPQExpBuffer(buf);
268 80 : return connstr;
269 : }
270 :
271 : /*
272 : * executeQuery
273 : *
274 : * Run a query, return the results, exit program on failure.
275 : */
276 : PGresult *
277 1922 : executeQuery(PGconn *conn, const char *query)
278 : {
279 : PGresult *res;
280 :
281 1922 : pg_log_info("executing %s", query);
282 :
283 1922 : res = PQexec(conn, query);
284 3844 : if (!res ||
285 1922 : PQresultStatus(res) != PGRES_TUPLES_OK)
286 : {
287 0 : pg_log_error("query failed: %s", PQerrorMessage(conn));
288 0 : pg_log_error_detail("Query was: %s", query);
289 0 : PQfinish(conn);
290 0 : exit_nicely(1);
291 : }
292 :
293 1922 : return res;
294 : }
|