1 /*-------------------------------------------------------------------------
5 * Implements the basic DB functions used by the archiver.
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
);
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
)
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
);
74 die_horribly(AH
, NULL
, "aborting because of server version mismatch\n");
79 * Reconnect to the server. If dbname is not NULL, use that database,
80 * else the one associated with the archive handle. If username is
81 * not NULL, use that user name, else the one from the handle. If
82 * both the database and the user match the existing connection already,
83 * nothing will be done.
85 * Returns 1 in any case.
88 ReconnectToServer(ArchiveHandle
*AH
, const char *dbname
, const char *username
)
91 const char *newdbname
;
92 const char *newusername
;
95 newdbname
= PQdb(AH
->connection
);
100 newusername
= PQuser(AH
->connection
);
102 newusername
= username
;
104 /* Let's see if the request is already satisfied */
105 if (strcmp(newdbname
, PQdb(AH
->connection
)) == 0 &&
106 strcmp(newusername
, PQuser(AH
->connection
)) == 0)
109 newConn
= _connectDB(AH
, newdbname
, newusername
);
111 PQfinish(AH
->connection
);
112 AH
->connection
= newConn
;
118 * Connect to the db again.
120 * Note: it's not really all that sensible to use a single-entry password
121 * cache if the username keeps changing. In current usage, however, the
122 * username never does change, so one savedPassword is sufficient. We do
123 * update the cache on the off chance that the password has changed since the
127 _connectDB(ArchiveHandle
*AH
, const char *reqdb
, const char *requser
)
132 char *password
= AH
->savedPassword
;
136 newdb
= PQdb(AH
->connection
);
140 if (!requser
|| strlen(requser
) == 0)
141 newuser
= PQuser(AH
->connection
);
145 ahlog(AH
, 1, "connecting to database \"%s\" as user \"%s\"\n",
148 if (AH
->promptPassword
== TRI_YES
&& password
== NULL
)
150 password
= simple_prompt("Password: ", 100, false);
151 if (password
== NULL
)
152 die_horribly(AH
, modulename
, "out of memory\n");
158 newConn
= PQsetdbLogin(PQhost(AH
->connection
), PQport(AH
->connection
),
162 die_horribly(AH
, modulename
, "failed to reconnect to database\n");
164 if (PQstatus(newConn
) == CONNECTION_BAD
)
166 if (!PQconnectionNeedsPassword(newConn
))
167 die_horribly(AH
, modulename
, "could not reconnect to database: %s",
168 PQerrorMessage(newConn
));
172 fprintf(stderr
, "Password incorrect\n");
174 fprintf(stderr
, "Connecting to %s as %s\n",
180 if (AH
->promptPassword
!= TRI_NO
)
181 password
= simple_prompt("Password: ", 100, false);
183 die_horribly(AH
, modulename
, "connection needs password\n");
185 if (password
== NULL
)
186 die_horribly(AH
, modulename
, "out of memory\n");
191 AH
->savedPassword
= password
;
193 /* check for version mismatch */
194 _check_database_version(AH
);
196 PQsetNoticeProcessor(newConn
, notice_processor
, NULL
);
203 * Make a database connection with the given parameters. The
204 * connection handle is returned, the parameters are stored in AHX.
205 * An interactive password prompt is automatically issued if required.
207 * Note: it's not really all that sensible to use a single-entry password
208 * cache if the username keeps changing. In current usage, however, the
209 * username never does change, so one savedPassword is sufficient.
212 ConnectDatabase(Archive
*AHX
,
216 const char *username
,
217 enum trivalue prompt_password
)
219 ArchiveHandle
*AH
= (ArchiveHandle
*) AHX
;
220 char *password
= AH
->savedPassword
;
224 die_horribly(AH
, modulename
, "already connected to a database\n");
226 if (prompt_password
== TRI_YES
&& password
== NULL
)
228 password
= simple_prompt("Password: ", 100, false);
229 if (password
== NULL
)
230 die_horribly(AH
, modulename
, "out of memory\n");
232 AH
->promptPassword
= prompt_password
;
235 * Start the connection. Loop until we have a password if requested by
241 AH
->connection
= PQsetdbLogin(pghost
, pgport
, NULL
, NULL
,
242 dbname
, username
, password
);
245 die_horribly(AH
, modulename
, "failed to connect to database\n");
247 if (PQstatus(AH
->connection
) == CONNECTION_BAD
&&
248 PQconnectionNeedsPassword(AH
->connection
) &&
250 prompt_password
!= TRI_NO
)
252 PQfinish(AH
->connection
);
253 password
= simple_prompt("Password: ", 100, false);
254 if (password
== NULL
)
255 die_horribly(AH
, modulename
, "out of memory\n");
260 AH
->savedPassword
= password
;
262 /* check to see that the backend connection was successfully made */
263 if (PQstatus(AH
->connection
) == CONNECTION_BAD
)
264 die_horribly(AH
, modulename
, "connection to database \"%s\" failed: %s",
265 PQdb(AH
->connection
), PQerrorMessage(AH
->connection
));
267 /* check for version mismatch */
268 _check_database_version(AH
);
270 PQsetNoticeProcessor(AH
->connection
, notice_processor
, NULL
);
272 return AH
->connection
;
277 notice_processor(void *arg
, const char *message
)
279 write_msg(NULL
, "%s", message
);
283 /* Public interface */
284 /* Convenience function to send a query. Monitors result to handle COPY statements */
286 ExecuteSqlCommand(ArchiveHandle
*AH
, const char *qry
, const char *desc
)
288 PGconn
*conn
= AH
->connection
;
290 char errStmt
[DB_MAX_ERR_STMT
];
293 fprintf(stderr
, "Executing: '%s'\n\n", qry
);
295 res
= PQexec(conn
, qry
);
297 switch (PQresultStatus(res
))
299 case PGRES_COMMAND_OK
:
300 case PGRES_TUPLES_OK
:
304 /* Assume this is an expected result */
309 strncpy(errStmt
, qry
, DB_MAX_ERR_STMT
);
310 if (errStmt
[DB_MAX_ERR_STMT
- 1] != '\0')
312 errStmt
[DB_MAX_ERR_STMT
- 4] = '.';
313 errStmt
[DB_MAX_ERR_STMT
- 3] = '.';
314 errStmt
[DB_MAX_ERR_STMT
- 2] = '.';
315 errStmt
[DB_MAX_ERR_STMT
- 1] = '\0';
317 warn_or_die_horribly(AH
, modulename
, "%s: %s Command was: %s\n",
318 desc
, PQerrorMessage(conn
), errStmt
);
326 * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
329 _sendCopyLine(ArchiveHandle
*AH
, char *qry
, char *eos
)
331 size_t loc
; /* Location of next newline */
332 int pos
= 0; /* Current position */
333 int sPos
= 0; /* Last pos of a slash char */
336 /* loop to find unquoted newline ending the line of COPY data */
339 loc
= strcspn(&qry
[pos
], "\n") + pos
;
341 /* If no match, then wait */
342 if (loc
>= (eos
- qry
)) /* None found */
344 appendBinaryPQExpBuffer(AH
->pgCopyBuf
, qry
, (eos
- qry
));
349 * fprintf(stderr, "Found cr at %d, prev char was %c, next was %c\n",
350 * loc, qry[loc-1], qry[loc+1]);
353 /* Count the number of preceding slashes */
355 while (sPos
> 0 && qry
[sPos
- 1] == '\\')
361 * If an odd number of preceding slashes, then \n was escaped so set
362 * the next search pos, and loop (if any left).
366 /* fprintf(stderr, "cr was escaped\n"); */
368 if (pos
>= (eos
- qry
))
370 appendBinaryPQExpBuffer(AH
->pgCopyBuf
, qry
, (eos
- qry
));
378 /* We found an unquoted newline */
380 appendPQExpBuffer(AH
->pgCopyBuf
, "%s\n", qry
);
381 isEnd
= (strcmp(AH
->pgCopyBuf
->data
, "\\.\n") == 0);
384 * Note that we drop the data on the floor if libpq has failed to enter
385 * COPY mode; this allows us to behave reasonably when trying to continue
386 * after an error in a COPY command.
389 PQputCopyData(AH
->connection
, AH
->pgCopyBuf
->data
,
390 AH
->pgCopyBuf
->len
) <= 0)
391 die_horribly(AH
, modulename
, "error returned by PQputCopyData: %s",
392 PQerrorMessage(AH
->connection
));
394 resetPQExpBuffer(AH
->pgCopyBuf
);
396 if (isEnd
&& AH
->pgCopyIn
)
400 if (PQputCopyEnd(AH
->connection
, NULL
) <= 0)
401 die_horribly(AH
, modulename
, "error returned by PQputCopyEnd: %s",
402 PQerrorMessage(AH
->connection
));
404 /* Check command status and return to normal libpq state */
405 res
= PQgetResult(AH
->connection
);
406 if (PQresultStatus(res
) != PGRES_COMMAND_OK
)
407 warn_or_die_horribly(AH
, modulename
, "COPY failed: %s",
408 PQerrorMessage(AH
->connection
));
411 AH
->pgCopyIn
= false;
414 return qry
+ loc
+ 1;
418 * Used by ExecuteSqlCommandBuf to send one buffered line of SQL
419 * (not data for the copy command).
422 _sendSQLLine(ArchiveHandle
*AH
, char *qry
, char *eos
)
425 * The following is a mini state machine to assess the end of an SQL
426 * statement. It really only needs to parse good SQL, or at least that's
427 * the theory... End-of-statement is assumed to be an unquoted,
428 * un-commented semi-colon that's not within any parentheses.
430 * Note: the input can be split into bufferloads at arbitrary boundaries.
431 * Therefore all state must be kept in AH->sqlparse, not in local
432 * variables of this routine. We assume that AH->sqlparse was filled with
433 * zeroes when created.
435 for (; qry
< eos
; qry
++)
437 switch (AH
->sqlparse
.state
)
439 case SQL_SCAN
: /* Default state == 0, set in _allocAH */
440 if (*qry
== ';' && AH
->sqlparse
.braceDepth
== 0)
443 * We've found the end of a statement. Send it and reset
446 appendPQExpBufferChar(AH
->sqlBuf
, ';'); /* inessential */
447 ExecuteSqlCommand(AH
, AH
->sqlBuf
->data
,
448 "could not execute query");
449 resetPQExpBuffer(AH
->sqlBuf
);
450 AH
->sqlparse
.lastChar
= '\0';
453 * Remove any following newlines - so that embedded COPY
454 * commands don't get a starting newline.
457 while (qry
< eos
&& *qry
== '\n')
460 /* We've finished one line, so exit */
463 else if (*qry
== '\'')
465 if (AH
->sqlparse
.lastChar
== 'E')
466 AH
->sqlparse
.state
= SQL_IN_E_QUOTE
;
468 AH
->sqlparse
.state
= SQL_IN_SINGLE_QUOTE
;
469 AH
->sqlparse
.backSlash
= false;
471 else if (*qry
== '"')
473 AH
->sqlparse
.state
= SQL_IN_DOUBLE_QUOTE
;
477 * Look for dollar-quotes. We make the assumption that
478 * $-quotes will not have an ident character just before them
479 * in pg_dump output. XXX is this good enough?
481 else if (*qry
== '$' && !_isIdentChar(AH
->sqlparse
.lastChar
))
483 AH
->sqlparse
.state
= SQL_IN_DOLLAR_TAG
;
484 /* initialize separate buffer with possible tag */
485 if (AH
->sqlparse
.tagBuf
== NULL
)
486 AH
->sqlparse
.tagBuf
= createPQExpBuffer();
488 resetPQExpBuffer(AH
->sqlparse
.tagBuf
);
489 appendPQExpBufferChar(AH
->sqlparse
.tagBuf
, *qry
);
491 else if (*qry
== '-' && AH
->sqlparse
.lastChar
== '-')
492 AH
->sqlparse
.state
= SQL_IN_SQL_COMMENT
;
493 else if (*qry
== '*' && AH
->sqlparse
.lastChar
== '/')
494 AH
->sqlparse
.state
= SQL_IN_EXT_COMMENT
;
495 else if (*qry
== '(')
496 AH
->sqlparse
.braceDepth
++;
497 else if (*qry
== ')')
498 AH
->sqlparse
.braceDepth
--;
501 case SQL_IN_SQL_COMMENT
:
503 AH
->sqlparse
.state
= SQL_SCAN
;
506 case SQL_IN_EXT_COMMENT
:
509 * This isn't fully correct, because we don't account for
510 * nested slash-stars, but pg_dump never emits such.
512 if (AH
->sqlparse
.lastChar
== '*' && *qry
== '/')
513 AH
->sqlparse
.state
= SQL_SCAN
;
516 case SQL_IN_SINGLE_QUOTE
:
517 /* We needn't handle '' specially */
518 if (*qry
== '\'' && !AH
->sqlparse
.backSlash
)
519 AH
->sqlparse
.state
= SQL_SCAN
;
520 else if (*qry
== '\\')
521 AH
->sqlparse
.backSlash
= !AH
->sqlparse
.backSlash
;
523 AH
->sqlparse
.backSlash
= false;
529 * Eventually we will need to handle '' specially, because
530 * after E'...''... we should still be in E_QUOTE state.
532 * XXX problem: how do we tell whether the dump was made by a
533 * version that thinks backslashes aren't special in non-E
536 if (*qry
== '\'' && !AH
->sqlparse
.backSlash
)
537 AH
->sqlparse
.state
= SQL_SCAN
;
538 else if (*qry
== '\\')
539 AH
->sqlparse
.backSlash
= !AH
->sqlparse
.backSlash
;
541 AH
->sqlparse
.backSlash
= false;
544 case SQL_IN_DOUBLE_QUOTE
:
545 /* We needn't handle "" specially */
547 AH
->sqlparse
.state
= SQL_SCAN
;
550 case SQL_IN_DOLLAR_TAG
:
553 /* Do not add the closing $ to tagBuf */
554 AH
->sqlparse
.state
= SQL_IN_DOLLAR_QUOTE
;
555 AH
->sqlparse
.minTagEndPos
= AH
->sqlBuf
->len
+ AH
->sqlparse
.tagBuf
->len
+ 1;
557 else if (_isDQChar(*qry
, (AH
->sqlparse
.tagBuf
->len
== 1)))
559 /* Valid, so add to tag */
560 appendPQExpBufferChar(AH
->sqlparse
.tagBuf
, *qry
);
565 * Ooops, we're not really in a dollar-tag. Valid tag
566 * chars do not include the various chars we look for in
567 * this state machine, so it's safe to just jump from this
568 * state back to SCAN. We have to back up the qry pointer
569 * so that the current character gets rescanned in SCAN
570 * state; and then "continue" so that the bottom-of-loop
571 * actions aren't done yet.
573 AH
->sqlparse
.state
= SQL_SCAN
;
579 case SQL_IN_DOLLAR_QUOTE
:
582 * If we are at a $, see whether what precedes it matches
583 * tagBuf. (Remember that the trailing $ of the tag was not
584 * added to tagBuf.) However, don't compare until we have
585 * enough data to be a possible match --- this is needed to
586 * avoid false match on '$a$a$...'
589 AH
->sqlBuf
->len
>= AH
->sqlparse
.minTagEndPos
&&
590 strcmp(AH
->sqlparse
.tagBuf
->data
,
591 AH
->sqlBuf
->data
+ AH
->sqlBuf
->len
- AH
->sqlparse
.tagBuf
->len
) == 0)
592 AH
->sqlparse
.state
= SQL_SCAN
;
596 appendPQExpBufferChar(AH
->sqlBuf
, *qry
);
597 AH
->sqlparse
.lastChar
= *qry
;
601 * If we get here, we've processed entire bufferload with no complete SQL
608 /* Convenience function to send one or more queries. Monitors result to handle COPY statements */
610 ExecuteSqlCommandBuf(ArchiveHandle
*AH
, void *qryv
, size_t bufLen
)
612 char *qry
= (char *) qryv
;
613 char *eos
= qry
+ bufLen
;
616 * fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n",
620 /* Could switch between command and COPY IN mode at each line */
624 * If libpq is in CopyIn mode *or* if the archive structure shows we
625 * are sending COPY data, treat the data as COPY data. The pgCopyIn
626 * check is only needed for backwards compatibility with ancient
627 * archive files that might just issue a COPY command without marking
628 * it properly. Note that in an archive entry that has a copyStmt,
629 * all data up to the end of the entry will go to _sendCopyLine, and
630 * therefore will be dropped if libpq has failed to enter COPY mode.
631 * Also, if a "\." data terminator is found, anything remaining in the
632 * archive entry will be dropped.
634 if (AH
->pgCopyIn
|| AH
->writingCopyData
)
635 qry
= _sendCopyLine(AH
, qry
, eos
);
637 qry
= _sendSQLLine(AH
, qry
, eos
);
644 StartTransaction(ArchiveHandle
*AH
)
646 ExecuteSqlCommand(AH
, "BEGIN", "could not start database transaction");
650 CommitTransaction(ArchiveHandle
*AH
)
652 ExecuteSqlCommand(AH
, "COMMIT", "could not commit database transaction");
656 _isIdentChar(unsigned char c
)
658 if ((c
>= 'a' && c
<= 'z')
659 || (c
>= 'A' && c
<= 'Z')
660 || (c
>= '0' && c
<= '9')
663 || (c
>= (unsigned char) '\200') /* no need to check <= \377 */
671 _isDQChar(unsigned char c
, bool atStart
)
673 if ((c
>= 'a' && c
<= 'z')
674 || (c
>= 'A' && c
<= 'Z')
676 || (!atStart
&& c
>= '0' && c
<= '9')
677 || (c
>= (unsigned char) '\200') /* no need to check <= \377 */