1 /*-------------------------------------------------------------------------
5 * Implements the basic DB functions used by the archiver.
8 * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.77 2007/12/09 19:01:40 tgl Exp $
10 *-------------------------------------------------------------------------
13 #include "pg_backup_db.h"
14 #include "dumputils.h"
25 static const char *modulename = gettext_noop("archiver (db)");
27 static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
28 static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
29 static void notice_processor(void *arg, const char *message);
30 static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos);
31 static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos);
33 static bool _isIdentChar(unsigned char c);
34 static bool _isDQChar(unsigned char c, bool atStart);
36 #define DB_MAX_ERR_STMT 128
39 _parse_version(ArchiveHandle *AH, const char *versionString)
43 v = parse_version(versionString);
45 die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
51 _check_database_version(ArchiveHandle *AH, bool ignoreVersion)
54 const char *remoteversion_str;
57 myversion = _parse_version(AH, PG_VERSION);
59 remoteversion_str = PQparameterStatus(AH->connection, "server_version");
60 if (!remoteversion_str)
61 die_horribly(AH, modulename, "could not get server_version from libpq\n");
63 remoteversion = _parse_version(AH, remoteversion_str);
65 AH->public.remoteVersionStr = strdup(remoteversion_str);
66 AH->public.remoteVersion = remoteversion;
68 if (myversion != remoteversion
69 && (remoteversion < AH->public.minRemoteVersion ||
70 remoteversion > AH->public.maxRemoteVersion))
72 write_msg(NULL, "server version: %s; %s version: %s\n",
73 remoteversion_str, progname, PG_VERSION);
75 write_msg(NULL, "proceeding despite version mismatch\n");
77 die_horribly(AH, NULL, "aborting because of version mismatch (Use the -i option to proceed anyway.)\n");
82 * Reconnect to the server. If dbname is not NULL, use that database,
83 * else the one associated with the archive handle. If username is
84 * not NULL, use that user name, else the one from the handle. If
85 * both the database and the user match the existing connection already,
86 * nothing will be done.
88 * Returns 1 in any case.
91 ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
94 const char *newdbname;
95 const char *newusername;
98 newdbname = PQdb(AH->connection);
103 newusername = PQuser(AH->connection);
105 newusername = username;
107 /* Let's see if the request is already satisfied */
108 if (strcmp(newdbname, PQdb(AH->connection)) == 0 &&
109 strcmp(newusername, PQuser(AH->connection)) == 0)
112 newConn = _connectDB(AH, newdbname, newusername);
114 PQfinish(AH->connection);
115 AH->connection = newConn;
121 * Connect to the db again.
124 _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
129 char *password = NULL;
133 newdb = PQdb(AH->connection);
135 newdb = (char *) reqdb;
137 if (!requser || (strlen(requser) == 0))
138 newuser = PQuser(AH->connection);
140 newuser = (char *) requser;
142 ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n", newdb, newuser);
144 if (AH->requirePassword)
146 password = simple_prompt("Password: ", 100, false);
147 if (password == NULL)
148 die_horribly(AH, modulename, "out of memory\n");
154 newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
158 die_horribly(AH, modulename, "failed to reconnect to database\n");
160 if (PQstatus(newConn) == CONNECTION_BAD)
162 if (!PQconnectionNeedsPassword(newConn))
163 die_horribly(AH, modulename, "could not reconnect to database: %s",
164 PQerrorMessage(newConn));
168 fprintf(stderr, "Password incorrect\n");
170 fprintf(stderr, "Connecting to %s as %s\n",
175 password = simple_prompt("Password: ", 100, false);
183 /* check for version mismatch */
184 _check_database_version(AH, true);
186 PQsetNoticeProcessor(newConn, notice_processor, NULL);
193 * Make a database connection with the given parameters. The
194 * connection handle is returned, the parameters are stored in AHX.
195 * An interactive password prompt is automatically issued if required.
198 ConnectDatabase(Archive *AHX,
202 const char *username,
204 const int ignoreVersion)
206 ArchiveHandle *AH = (ArchiveHandle *) AHX;
207 char *password = NULL;
211 die_horribly(AH, modulename, "already connected to a database\n");
215 password = simple_prompt("Password: ", 100, false);
216 if (password == NULL)
217 die_horribly(AH, modulename, "out of memory\n");
218 AH->requirePassword = true;
221 AH->requirePassword = false;
224 * Start the connection. Loop until we have a password if requested by
230 AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
231 dbname, username, password);
234 die_horribly(AH, modulename, "failed to connect to database\n");
236 if (PQstatus(AH->connection) == CONNECTION_BAD &&
237 PQconnectionNeedsPassword(AH->connection) &&
241 PQfinish(AH->connection);
242 password = simple_prompt("Password: ", 100, false);
250 /* check to see that the backend connection was successfully made */
251 if (PQstatus(AH->connection) == CONNECTION_BAD)
252 die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
253 PQdb(AH->connection), PQerrorMessage(AH->connection));
255 /* check for version mismatch */
256 _check_database_version(AH, ignoreVersion);
258 PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
260 return AH->connection;
265 notice_processor(void *arg, const char *message)
267 write_msg(NULL, "%s", message);
271 /* Public interface */
272 /* Convenience function to send a query. Monitors result to handle COPY statements */
274 ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc)
276 PGconn *conn = AH->connection;
278 char errStmt[DB_MAX_ERR_STMT];
280 /* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
281 res = PQexec(conn, qry->data);
283 die_horribly(AH, modulename, "%s: no result from server\n", desc);
285 if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
287 if (PQresultStatus(res) == PGRES_COPY_IN)
293 strncpy(errStmt, qry->data, DB_MAX_ERR_STMT);
294 if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
296 errStmt[DB_MAX_ERR_STMT - 4] = '.';
297 errStmt[DB_MAX_ERR_STMT - 3] = '.';
298 errStmt[DB_MAX_ERR_STMT - 2] = '.';
299 errStmt[DB_MAX_ERR_STMT - 1] = '\0';
301 warn_or_die_horribly(AH, modulename, "%s: %s Command was: %s\n",
302 desc, PQerrorMessage(AH->connection),
309 return strlen(qry->data);
313 * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
316 _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
318 size_t loc; /* Location of next newline */
319 int pos = 0; /* Current position */
320 int sPos = 0; /* Last pos of a slash char */
323 /* loop to find unquoted newline ending the line of COPY data */
326 loc = strcspn(&qry[pos], "\n") + pos;
328 /* If no match, then wait */
329 if (loc >= (eos - qry)) /* None found */
331 appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
336 * fprintf(stderr, "Found cr at %d, prev char was %c, next was %c\n",
337 * loc, qry[loc-1], qry[loc+1]);
340 /* Count the number of preceding slashes */
342 while (sPos > 0 && qry[sPos - 1] == '\\')
348 * If an odd number of preceding slashes, then \n was escaped so set
349 * the next search pos, and loop (if any left).
353 /* fprintf(stderr, "cr was escaped\n"); */
355 if (pos >= (eos - qry))
357 appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
365 /* We found an unquoted newline */
367 appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
368 isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
371 * Note that we drop the data on the floor if libpq has failed to enter
372 * COPY mode; this allows us to behave reasonably when trying to continue
373 * after an error in a COPY command.
376 PQputCopyData(AH->connection, AH->pgCopyBuf->data,
377 AH->pgCopyBuf->len) <= 0)
378 die_horribly(AH, modulename, "error returned by PQputCopyData: %s",
379 PQerrorMessage(AH->connection));
381 resetPQExpBuffer(AH->pgCopyBuf);
383 if (isEnd && AH->pgCopyIn)
387 if (PQputCopyEnd(AH->connection, NULL) <= 0)
388 die_horribly(AH, modulename, "error returned by PQputCopyEnd: %s",
389 PQerrorMessage(AH->connection));
391 /* Check command status and return to normal libpq state */
392 res = PQgetResult(AH->connection);
393 if (PQresultStatus(res) != PGRES_COMMAND_OK)
394 warn_or_die_horribly(AH, modulename, "COPY failed: %s",
395 PQerrorMessage(AH->connection));
398 AH->pgCopyIn = false;
401 return qry + loc + 1;
405 * Used by ExecuteSqlCommandBuf to send one buffered line of SQL
406 * (not data for the copy command).
409 _sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
412 * The following is a mini state machine to assess the end of an SQL
413 * statement. It really only needs to parse good SQL, or at least that's
414 * the theory... End-of-statement is assumed to be an unquoted,
415 * un-commented semi-colon that's not within any parentheses.
417 * Note: the input can be split into bufferloads at arbitrary boundaries.
418 * Therefore all state must be kept in AH->sqlparse, not in local
419 * variables of this routine. We assume that AH->sqlparse was filled with
420 * zeroes when created.
422 for (; qry < eos; qry++)
424 switch (AH->sqlparse.state)
426 case SQL_SCAN: /* Default state == 0, set in _allocAH */
427 if (*qry == ';' && AH->sqlparse.braceDepth == 0)
430 * We've found the end of a statement. Send it and reset
433 appendPQExpBufferChar(AH->sqlBuf, ';'); /* inessential */
434 ExecuteSqlCommand(AH, AH->sqlBuf,
435 "could not execute query");
436 resetPQExpBuffer(AH->sqlBuf);
437 AH->sqlparse.lastChar = '\0';
440 * Remove any following newlines - so that embedded COPY
441 * commands don't get a starting newline.
444 while (qry < eos && *qry == '\n')
447 /* We've finished one line, so exit */
450 else if (*qry == '\'')
452 if (AH->sqlparse.lastChar == 'E')
453 AH->sqlparse.state = SQL_IN_E_QUOTE;
455 AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
456 AH->sqlparse.backSlash = false;
458 else if (*qry == '"')
460 AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
464 * Look for dollar-quotes. We make the assumption that
465 * $-quotes will not have an ident character just before them
466 * in pg_dump output. XXX is this good enough?
468 else if (*qry == '$' && !_isIdentChar(AH->sqlparse.lastChar))
470 AH->sqlparse.state = SQL_IN_DOLLAR_TAG;
471 /* initialize separate buffer with possible tag */
472 if (AH->sqlparse.tagBuf == NULL)
473 AH->sqlparse.tagBuf = createPQExpBuffer();
475 resetPQExpBuffer(AH->sqlparse.tagBuf);
476 appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
478 else if (*qry == '-' && AH->sqlparse.lastChar == '-')
479 AH->sqlparse.state = SQL_IN_SQL_COMMENT;
480 else if (*qry == '*' && AH->sqlparse.lastChar == '/')
481 AH->sqlparse.state = SQL_IN_EXT_COMMENT;
482 else if (*qry == '(')
483 AH->sqlparse.braceDepth++;
484 else if (*qry == ')')
485 AH->sqlparse.braceDepth--;
488 case SQL_IN_SQL_COMMENT:
490 AH->sqlparse.state = SQL_SCAN;
493 case SQL_IN_EXT_COMMENT:
496 * This isn't fully correct, because we don't account for
497 * nested slash-stars, but pg_dump never emits such.
499 if (AH->sqlparse.lastChar == '*' && *qry == '/')
500 AH->sqlparse.state = SQL_SCAN;
503 case SQL_IN_SINGLE_QUOTE:
504 /* We needn't handle '' specially */
505 if (*qry == '\'' && !AH->sqlparse.backSlash)
506 AH->sqlparse.state = SQL_SCAN;
507 else if (*qry == '\\')
508 AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
510 AH->sqlparse.backSlash = false;
516 * Eventually we will need to handle '' specially, because
517 * after E'...''... we should still be in E_QUOTE state.
519 * XXX problem: how do we tell whether the dump was made by a
520 * version that thinks backslashes aren't special in non-E
523 if (*qry == '\'' && !AH->sqlparse.backSlash)
524 AH->sqlparse.state = SQL_SCAN;
525 else if (*qry == '\\')
526 AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
528 AH->sqlparse.backSlash = false;
531 case SQL_IN_DOUBLE_QUOTE:
532 /* We needn't handle "" specially */
534 AH->sqlparse.state = SQL_SCAN;
537 case SQL_IN_DOLLAR_TAG:
540 /* Do not add the closing $ to tagBuf */
541 AH->sqlparse.state = SQL_IN_DOLLAR_QUOTE;
542 AH->sqlparse.minTagEndPos = AH->sqlBuf->len + AH->sqlparse.tagBuf->len + 1;
544 else if (_isDQChar(*qry, (AH->sqlparse.tagBuf->len == 1)))
546 /* Valid, so add to tag */
547 appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
552 * Ooops, we're not really in a dollar-tag. Valid tag
553 * chars do not include the various chars we look for in
554 * this state machine, so it's safe to just jump from this
555 * state back to SCAN. We have to back up the qry pointer
556 * so that the current character gets rescanned in SCAN
557 * state; and then "continue" so that the bottom-of-loop
558 * actions aren't done yet.
560 AH->sqlparse.state = SQL_SCAN;
566 case SQL_IN_DOLLAR_QUOTE:
569 * If we are at a $, see whether what precedes it matches
570 * tagBuf. (Remember that the trailing $ of the tag was not
571 * added to tagBuf.) However, don't compare until we have
572 * enough data to be a possible match --- this is needed to
573 * avoid false match on '$a$a$...'
576 AH->sqlBuf->len >= AH->sqlparse.minTagEndPos &&
577 strcmp(AH->sqlparse.tagBuf->data,
578 AH->sqlBuf->data + AH->sqlBuf->len - AH->sqlparse.tagBuf->len) == 0)
579 AH->sqlparse.state = SQL_SCAN;
583 appendPQExpBufferChar(AH->sqlBuf, *qry);
584 AH->sqlparse.lastChar = *qry;
588 * If we get here, we've processed entire bufferload with no complete SQL
595 /* Convenience function to send one or more queries. Monitors result to handle COPY statements */
597 ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, size_t bufLen)
599 char *qry = (char *) qryv;
600 char *eos = qry + bufLen;
603 * fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n",
607 /* Could switch between command and COPY IN mode at each line */
611 * If libpq is in CopyIn mode *or* if the archive structure shows we
612 * are sending COPY data, treat the data as COPY data. The pgCopyIn
613 * check is only needed for backwards compatibility with ancient
614 * archive files that might just issue a COPY command without marking
615 * it properly. Note that in an archive entry that has a copyStmt,
616 * all data up to the end of the entry will go to _sendCopyLine, and
617 * therefore will be dropped if libpq has failed to enter COPY mode.
618 * Also, if a "\." data terminator is found, anything remaining in the
619 * archive entry will be dropped.
621 if (AH->pgCopyIn || AH->writingCopyData)
622 qry = _sendCopyLine(AH, qry, eos);
624 qry = _sendSQLLine(AH, qry, eos);
631 StartTransaction(ArchiveHandle *AH)
633 PQExpBuffer qry = createPQExpBuffer();
635 appendPQExpBuffer(qry, "BEGIN");
637 ExecuteSqlCommand(AH, qry, "could not start database transaction");
639 destroyPQExpBuffer(qry);
643 CommitTransaction(ArchiveHandle *AH)
645 PQExpBuffer qry = createPQExpBuffer();
647 appendPQExpBuffer(qry, "COMMIT");
649 ExecuteSqlCommand(AH, qry, "could not commit database transaction");
651 destroyPQExpBuffer(qry);
655 _isIdentChar(unsigned char c)
657 if ((c >= 'a' && c <= 'z')
658 || (c >= 'A' && c <= 'Z')
659 || (c >= '0' && c <= '9')
662 || (c >= (unsigned char) '\200') /* no need to check <= \377 */
670 _isDQChar(unsigned char c, bool atStart)
672 if ((c >= 'a' && c <= 'z')
673 || (c >= 'A' && c <= 'Z')
675 || (!atStart && c >= '0' && c <= '9')
676 || (c >= (unsigned char) '\200') /* no need to check <= \377 */