1 /*-------------------------------------------------------------------------
5 * Implements the basic DB functions used by the archiver.
8 * src/bin/pg_dump/pg_backup_db.c
10 *-------------------------------------------------------------------------
13 #include "pg_backup_db.h"
14 #include "dumputils.h"
23 #define DB_MAX_ERR_STMT 128
25 static const char *modulename = gettext_noop("archiver (db)");
27 static void _check_database_version(ArchiveHandle *AH);
28 static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
29 static void notice_processor(void *arg, const char *message);
32 _parse_version(ArchiveHandle *AH, const char *versionString)
36 v = parse_version(versionString);
38 die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
44 _check_database_version(ArchiveHandle *AH)
47 const char *remoteversion_str;
50 myversion = _parse_version(AH, PG_VERSION);
52 remoteversion_str = PQparameterStatus(AH->connection, "server_version");
53 if (!remoteversion_str)
54 die_horribly(AH, modulename, "could not get server_version from libpq\n");
56 remoteversion = _parse_version(AH, remoteversion_str);
58 AH->public.remoteVersionStr = strdup(remoteversion_str);
59 AH->public.remoteVersion = remoteversion;
60 if (!AH->archiveRemoteVersion)
61 AH->archiveRemoteVersion = AH->public.remoteVersionStr;
63 if (myversion != remoteversion
64 && (remoteversion < AH->public.minRemoteVersion ||
65 remoteversion > AH->public.maxRemoteVersion))
67 write_msg(NULL, "server version: %s; %s version: %s\n",
68 remoteversion_str, progname, PG_VERSION);
69 die_horribly(AH, NULL, "aborting because of server version mismatch\n");
74 * Reconnect to the server. If dbname is not NULL, use that database,
75 * else the one associated with the archive handle. If username is
76 * not NULL, use that user name, else the one from the handle. If
77 * both the database and the user match the existing connection already,
78 * nothing will be done.
80 * Returns 1 in any case.
83 ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
86 const char *newdbname;
87 const char *newusername;
90 newdbname = PQdb(AH->connection);
95 newusername = PQuser(AH->connection);
97 newusername = username;
99 /* Let's see if the request is already satisfied */
100 if (strcmp(newdbname, PQdb(AH->connection)) == 0 &&
101 strcmp(newusername, PQuser(AH->connection)) == 0)
104 newConn = _connectDB(AH, newdbname, newusername);
106 PQfinish(AH->connection);
107 AH->connection = newConn;
113 * Connect to the db again.
115 * Note: it's not really all that sensible to use a single-entry password
116 * cache if the username keeps changing. In current usage, however, the
117 * username never does change, so one savedPassword is sufficient. We do
118 * update the cache on the off chance that the password has changed since the
122 _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
127 char *password = AH->savedPassword;
131 newdb = PQdb(AH->connection);
135 if (!requser || strlen(requser) == 0)
136 newuser = PQuser(AH->connection);
140 ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n",
143 if (AH->promptPassword == TRI_YES && password == NULL)
145 password = simple_prompt("Password: ", 100, false);
146 if (password == NULL)
147 die_horribly(AH, modulename, "out of memory\n");
152 #define PARAMS_ARRAY_SIZE 7
153 const char **keywords = malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
154 const char **values = malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
156 if (!keywords || !values)
157 die_horribly(AH, modulename, "out of memory\n");
159 keywords[0] = "host";
160 values[0] = PQhost(AH->connection);
161 keywords[1] = "port";
162 values[1] = PQport(AH->connection);
163 keywords[2] = "user";
165 keywords[3] = "password";
166 values[3] = password;
167 keywords[4] = "dbname";
169 keywords[5] = "fallback_application_name";
170 values[5] = progname;
175 newConn = PQconnectdbParams(keywords, values, true);
181 die_horribly(AH, modulename, "failed to reconnect to database\n");
183 if (PQstatus(newConn) == CONNECTION_BAD)
185 if (!PQconnectionNeedsPassword(newConn))
186 die_horribly(AH, modulename, "could not reconnect to database: %s",
187 PQerrorMessage(newConn));
191 fprintf(stderr, "Password incorrect\n");
193 fprintf(stderr, "Connecting to %s as %s\n",
199 if (AH->promptPassword != TRI_NO)
200 password = simple_prompt("Password: ", 100, false);
202 die_horribly(AH, modulename, "connection needs password\n");
204 if (password == NULL)
205 die_horribly(AH, modulename, "out of memory\n");
210 AH->savedPassword = password;
212 /* check for version mismatch */
213 _check_database_version(AH);
215 PQsetNoticeProcessor(newConn, notice_processor, NULL);
222 * Make a database connection with the given parameters. The
223 * connection handle is returned, the parameters are stored in AHX.
224 * An interactive password prompt is automatically issued if required.
226 * Note: it's not really all that sensible to use a single-entry password
227 * cache if the username keeps changing. In current usage, however, the
228 * username never does change, so one savedPassword is sufficient.
231 ConnectDatabase(Archive *AHX,
235 const char *username,
236 enum trivalue prompt_password)
238 ArchiveHandle *AH = (ArchiveHandle *) AHX;
239 char *password = AH->savedPassword;
243 die_horribly(AH, modulename, "already connected to a database\n");
245 if (prompt_password == TRI_YES && password == NULL)
247 password = simple_prompt("Password: ", 100, false);
248 if (password == NULL)
249 die_horribly(AH, modulename, "out of memory\n");
251 AH->promptPassword = prompt_password;
254 * Start the connection. Loop until we have a password if requested by
259 #define PARAMS_ARRAY_SIZE 7
260 const char **keywords = malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
261 const char **values = malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
263 if (!keywords || !values)
264 die_horribly(AH, modulename, "out of memory\n");
266 keywords[0] = "host";
268 keywords[1] = "port";
270 keywords[2] = "user";
271 values[2] = username;
272 keywords[3] = "password";
273 values[3] = password;
274 keywords[4] = "dbname";
276 keywords[5] = "fallback_application_name";
277 values[5] = progname;
282 AH->connection = PQconnectdbParams(keywords, values, true);
288 die_horribly(AH, modulename, "failed to connect to database\n");
290 if (PQstatus(AH->connection) == CONNECTION_BAD &&
291 PQconnectionNeedsPassword(AH->connection) &&
293 prompt_password != TRI_NO)
295 PQfinish(AH->connection);
296 password = simple_prompt("Password: ", 100, false);
297 if (password == NULL)
298 die_horribly(AH, modulename, "out of memory\n");
303 AH->savedPassword = password;
305 /* check to see that the backend connection was successfully made */
306 if (PQstatus(AH->connection) == CONNECTION_BAD)
307 die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
308 PQdb(AH->connection), PQerrorMessage(AH->connection));
310 /* check for version mismatch */
311 _check_database_version(AH);
313 PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
315 return AH->connection;
320 notice_processor(void *arg, const char *message)
322 write_msg(NULL, "%s", message);
327 * Convenience function to send a query.
328 * Monitors result to detect COPY statements
331 ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
333 PGconn *conn = AH->connection;
335 char errStmt[DB_MAX_ERR_STMT];
338 fprintf(stderr, "Executing: '%s'\n\n", qry);
340 res = PQexec(conn, qry);
342 switch (PQresultStatus(res))
344 case PGRES_COMMAND_OK:
345 case PGRES_TUPLES_OK:
346 case PGRES_EMPTY_QUERY:
350 /* Assume this is an expected result */
355 strncpy(errStmt, qry, DB_MAX_ERR_STMT);
356 if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
358 errStmt[DB_MAX_ERR_STMT - 4] = '.';
359 errStmt[DB_MAX_ERR_STMT - 3] = '.';
360 errStmt[DB_MAX_ERR_STMT - 2] = '.';
361 errStmt[DB_MAX_ERR_STMT - 1] = '\0';
363 warn_or_die_horribly(AH, modulename, "%s: %s Command was: %s\n",
364 desc, PQerrorMessage(conn), errStmt);
373 * Implement ahwrite() for direct-to-DB restore
376 ExecuteSqlCommandBuf(ArchiveHandle *AH, const char *buf, size_t bufLen)
378 if (AH->writingCopyData)
381 * We drop the data on the floor if libpq has failed to enter COPY
382 * mode; this allows us to behave reasonably when trying to continue
383 * after an error in a COPY command.
386 PQputCopyData(AH->connection, buf, bufLen) <= 0)
387 die_horribly(AH, modulename, "error returned by PQputCopyData: %s",
388 PQerrorMessage(AH->connection));
393 * In most cases the data passed to us will be a null-terminated
394 * string, but if it's not, we have to add a trailing null.
396 if (buf[bufLen] == '\0')
397 ExecuteSqlCommand(AH, buf, "could not execute query");
400 char *str = (char *) malloc(bufLen + 1);
403 die_horribly(AH, modulename, "out of memory\n");
404 memcpy(str, buf, bufLen);
406 ExecuteSqlCommand(AH, str, "could not execute query");
415 * Terminate a COPY operation during direct-to-DB restore
418 EndDBCopyMode(ArchiveHandle *AH, TocEntry *te)
424 if (PQputCopyEnd(AH->connection, NULL) <= 0)
425 die_horribly(AH, modulename, "error returned by PQputCopyEnd: %s",
426 PQerrorMessage(AH->connection));
428 /* Check command status and return to normal libpq state */
429 res = PQgetResult(AH->connection);
430 if (PQresultStatus(res) != PGRES_COMMAND_OK)
431 warn_or_die_horribly(AH, modulename, "COPY failed for table \"%s\": %s",
432 te->tag, PQerrorMessage(AH->connection));
435 AH->pgCopyIn = false;
440 StartTransaction(ArchiveHandle *AH)
442 ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
446 CommitTransaction(ArchiveHandle *AH)
448 ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
452 DropBlobIfExists(ArchiveHandle *AH, Oid oid)
455 * If we are not restoring to a direct database connection, we have to
456 * guess about how to detect whether the blob exists. Assume new-style.
458 if (AH->connection == NULL ||
459 PQserverVersion(AH->connection) >= 90000)
462 "SELECT pg_catalog.lo_unlink(oid) "
463 "FROM pg_catalog.pg_largeobject_metadata "
464 "WHERE oid = '%u';\n",
469 /* Restoring to pre-9.0 server, so do it the old way */
471 "SELECT CASE WHEN EXISTS("
472 "SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u'"
473 ") THEN pg_catalog.lo_unlink('%u') END;\n",