]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_backup_db.c
d3e7959d8175e5771645c9b6545d0ced6bd55702
[postgresql] / src / bin / pg_dump / pg_backup_db.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_backup_db.c
4  *
5  *      Implements the basic DB functions used by the archiver.
6  *
7  * IDENTIFICATION
8  *        $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.77 2007/12/09 19:01:40 tgl Exp $
9  *
10  *-------------------------------------------------------------------------
11  */
12
13 #include "pg_backup_db.h"
14 #include "dumputils.h"
15
16 #include <unistd.h>
17
18 #include <ctype.h>
19
20 #ifdef HAVE_TERMIOS_H
21 #include <termios.h>
22 #endif
23
24
25 static const char *modulename = gettext_noop("archiver (db)");
26
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);
32
33 static bool _isIdentChar(unsigned char c);
34 static bool _isDQChar(unsigned char c, bool atStart);
35
36 #define DB_MAX_ERR_STMT 128
37
38 static int
39 _parse_version(ArchiveHandle *AH, const char *versionString)
40 {
41         int                     v;
42
43         v = parse_version(versionString);
44         if (v < 0)
45                 die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
46
47         return v;
48 }
49
50 static void
51 _check_database_version(ArchiveHandle *AH, bool ignoreVersion)
52 {
53         int                     myversion;
54         const char *remoteversion_str;
55         int                     remoteversion;
56
57         myversion = _parse_version(AH, PG_VERSION);
58
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");
62
63         remoteversion = _parse_version(AH, remoteversion_str);
64
65         AH->public.remoteVersionStr = strdup(remoteversion_str);
66         AH->public.remoteVersion = remoteversion;
67
68         if (myversion != remoteversion
69                 && (remoteversion < AH->public.minRemoteVersion ||
70                         remoteversion > AH->public.maxRemoteVersion))
71         {
72                 write_msg(NULL, "server version: %s; %s version: %s\n",
73                                   remoteversion_str, progname, PG_VERSION);
74                 if (ignoreVersion)
75                         write_msg(NULL, "proceeding despite version mismatch\n");
76                 else
77                         die_horribly(AH, NULL, "aborting because of version mismatch  (Use the -i option to proceed anyway.)\n");
78         }
79 }
80
81 /*
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.
87  *
88  * Returns 1 in any case.
89  */
90 int
91 ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
92 {
93         PGconn     *newConn;
94         const char *newdbname;
95         const char *newusername;
96
97         if (!dbname)
98                 newdbname = PQdb(AH->connection);
99         else
100                 newdbname = dbname;
101
102         if (!username)
103                 newusername = PQuser(AH->connection);
104         else
105                 newusername = username;
106
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)
110                 return 1;
111
112         newConn = _connectDB(AH, newdbname, newusername);
113
114         PQfinish(AH->connection);
115         AH->connection = newConn;
116
117         return 1;
118 }
119
120 /*
121  * Connect to the db again.
122  */
123 static PGconn *
124 _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
125 {
126         PGconn     *newConn;
127         char       *newdb;
128         char       *newuser;
129         char       *password = NULL;
130         bool            new_pass;
131
132         if (!reqdb)
133                 newdb = PQdb(AH->connection);
134         else
135                 newdb = (char *) reqdb;
136
137         if (!requser || (strlen(requser) == 0))
138                 newuser = PQuser(AH->connection);
139         else
140                 newuser = (char *) requser;
141
142         ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n", newdb, newuser);
143
144         if (AH->requirePassword)
145         {
146                 password = simple_prompt("Password: ", 100, false);
147                 if (password == NULL)
148                         die_horribly(AH, modulename, "out of memory\n");
149         }
150
151         do
152         {
153                 new_pass = false;
154                 newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
155                                                            NULL, NULL, newdb,
156                                                            newuser, password);
157                 if (!newConn)
158                         die_horribly(AH, modulename, "failed to reconnect to database\n");
159
160                 if (PQstatus(newConn) == CONNECTION_BAD)
161                 {
162                         if (!PQconnectionNeedsPassword(newConn))
163                                 die_horribly(AH, modulename, "could not reconnect to database: %s",
164                                                          PQerrorMessage(newConn));
165                         PQfinish(newConn);
166
167                         if (password)
168                                 fprintf(stderr, "Password incorrect\n");
169
170                         fprintf(stderr, "Connecting to %s as %s\n",
171                                         newdb, newuser);
172
173                         if (password)
174                                 free(password);
175                         password = simple_prompt("Password: ", 100, false);
176                         new_pass = true;
177                 }
178         } while (new_pass);
179
180         if (password)
181                 free(password);
182
183         /* check for version mismatch */
184         _check_database_version(AH, true);
185
186         PQsetNoticeProcessor(newConn, notice_processor, NULL);
187
188         return newConn;
189 }
190
191
192 /*
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.
196  */
197 PGconn *
198 ConnectDatabase(Archive *AHX,
199                                 const char *dbname,
200                                 const char *pghost,
201                                 const char *pgport,
202                                 const char *username,
203                                 const int reqPwd,
204                                 const int ignoreVersion)
205 {
206         ArchiveHandle *AH = (ArchiveHandle *) AHX;
207         char       *password = NULL;
208         bool            new_pass;
209
210         if (AH->connection)
211                 die_horribly(AH, modulename, "already connected to a database\n");
212
213         if (reqPwd)
214         {
215                 password = simple_prompt("Password: ", 100, false);
216                 if (password == NULL)
217                         die_horribly(AH, modulename, "out of memory\n");
218                 AH->requirePassword = true;
219         }
220         else
221                 AH->requirePassword = false;
222
223         /*
224          * Start the connection.  Loop until we have a password if requested by
225          * backend.
226          */
227         do
228         {
229                 new_pass = false;
230                 AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
231                                                                           dbname, username, password);
232
233                 if (!AH->connection)
234                         die_horribly(AH, modulename, "failed to connect to database\n");
235
236                 if (PQstatus(AH->connection) == CONNECTION_BAD &&
237                         PQconnectionNeedsPassword(AH->connection) &&
238                         password == NULL &&
239                         !feof(stdin))
240                 {
241                         PQfinish(AH->connection);
242                         password = simple_prompt("Password: ", 100, false);
243                         new_pass = true;
244                 }
245         } while (new_pass);
246
247         if (password)
248                 free(password);
249
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));
254
255         /* check for version mismatch */
256         _check_database_version(AH, ignoreVersion);
257
258         PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
259
260         return AH->connection;
261 }
262
263
264 static void
265 notice_processor(void *arg, const char *message)
266 {
267         write_msg(NULL, "%s", message);
268 }
269
270
271 /* Public interface */
272 /* Convenience function to send a query. Monitors result to handle COPY statements */
273 static int
274 ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc)
275 {
276         PGconn     *conn = AH->connection;
277         PGresult   *res;
278         char            errStmt[DB_MAX_ERR_STMT];
279
280         /* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
281         res = PQexec(conn, qry->data);
282         if (!res)
283                 die_horribly(AH, modulename, "%s: no result from server\n", desc);
284
285         if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
286         {
287                 if (PQresultStatus(res) == PGRES_COPY_IN)
288                 {
289                         AH->pgCopyIn = true;
290                 }
291                 else
292                 {
293                         strncpy(errStmt, qry->data, DB_MAX_ERR_STMT);
294                         if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
295                         {
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';
300                         }
301                         warn_or_die_horribly(AH, modulename, "%s: %s    Command was: %s\n",
302                                                                  desc, PQerrorMessage(AH->connection),
303                                                                  errStmt);
304                 }
305         }
306
307         PQclear(res);
308
309         return strlen(qry->data);
310 }
311
312 /*
313  * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
314  */
315 static char *
316 _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
317 {
318         size_t          loc;                    /* Location of next newline */
319         int                     pos = 0;                /* Current position */
320         int                     sPos = 0;               /* Last pos of a slash char */
321         int                     isEnd = 0;
322
323         /* loop to find unquoted newline ending the line of COPY data */
324         for (;;)
325         {
326                 loc = strcspn(&qry[pos], "\n") + pos;
327
328                 /* If no match, then wait */
329                 if (loc >= (eos - qry)) /* None found */
330                 {
331                         appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
332                         return eos;
333                 }
334
335                 /*
336                  * fprintf(stderr, "Found cr at %d, prev char was %c, next was %c\n",
337                  * loc, qry[loc-1], qry[loc+1]);
338                  */
339
340                 /* Count the number of preceding slashes */
341                 sPos = loc;
342                 while (sPos > 0 && qry[sPos - 1] == '\\')
343                         sPos--;
344
345                 sPos = loc - sPos;
346
347                 /*
348                  * If an odd number of preceding slashes, then \n was escaped so set
349                  * the next search pos, and loop (if any left).
350                  */
351                 if ((sPos & 1) == 1)
352                 {
353                         /* fprintf(stderr, "cr was escaped\n"); */
354                         pos = loc + 1;
355                         if (pos >= (eos - qry))
356                         {
357                                 appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
358                                 return eos;
359                         }
360                 }
361                 else
362                         break;
363         }
364
365         /* We found an unquoted newline */
366         qry[loc] = '\0';
367         appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
368         isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
369
370         /*
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.
374          */
375         if (AH->pgCopyIn &&
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));
380
381         resetPQExpBuffer(AH->pgCopyBuf);
382
383         if (isEnd && AH->pgCopyIn)
384         {
385                 PGresult   *res;
386
387                 if (PQputCopyEnd(AH->connection, NULL) <= 0)
388                         die_horribly(AH, modulename, "error returned by PQputCopyEnd: %s",
389                                                  PQerrorMessage(AH->connection));
390
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));
396                 PQclear(res);
397
398                 AH->pgCopyIn = false;
399         }
400
401         return qry + loc + 1;
402 }
403
404 /*
405  * Used by ExecuteSqlCommandBuf to send one buffered line of SQL
406  * (not data for the copy command).
407  */
408 static char *
409 _sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
410 {
411         /*
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.
416          *
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.
421          */
422         for (; qry < eos; qry++)
423         {
424                 switch (AH->sqlparse.state)
425                 {
426                         case SQL_SCAN:          /* Default state == 0, set in _allocAH */
427                                 if (*qry == ';' && AH->sqlparse.braceDepth == 0)
428                                 {
429                                         /*
430                                          * We've found the end of a statement. Send it and reset
431                                          * the buffer.
432                                          */
433                                         appendPQExpBufferChar(AH->sqlBuf, ';');         /* inessential */
434                                         ExecuteSqlCommand(AH, AH->sqlBuf,
435                                                                           "could not execute query");
436                                         resetPQExpBuffer(AH->sqlBuf);
437                                         AH->sqlparse.lastChar = '\0';
438
439                                         /*
440                                          * Remove any following newlines - so that embedded COPY
441                                          * commands don't get a starting newline.
442                                          */
443                                         qry++;
444                                         while (qry < eos && *qry == '\n')
445                                                 qry++;
446
447                                         /* We've finished one line, so exit */
448                                         return qry;
449                                 }
450                                 else if (*qry == '\'')
451                                 {
452                                         if (AH->sqlparse.lastChar == 'E')
453                                                 AH->sqlparse.state = SQL_IN_E_QUOTE;
454                                         else
455                                                 AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
456                                         AH->sqlparse.backSlash = false;
457                                 }
458                                 else if (*qry == '"')
459                                 {
460                                         AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
461                                 }
462
463                                 /*
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?
467                                  */
468                                 else if (*qry == '$' && !_isIdentChar(AH->sqlparse.lastChar))
469                                 {
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();
474                                         else
475                                                 resetPQExpBuffer(AH->sqlparse.tagBuf);
476                                         appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
477                                 }
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--;
486                                 break;
487
488                         case SQL_IN_SQL_COMMENT:
489                                 if (*qry == '\n')
490                                         AH->sqlparse.state = SQL_SCAN;
491                                 break;
492
493                         case SQL_IN_EXT_COMMENT:
494
495                                 /*
496                                  * This isn't fully correct, because we don't account for
497                                  * nested slash-stars, but pg_dump never emits such.
498                                  */
499                                 if (AH->sqlparse.lastChar == '*' && *qry == '/')
500                                         AH->sqlparse.state = SQL_SCAN;
501                                 break;
502
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;
509                                 else
510                                         AH->sqlparse.backSlash = false;
511                                 break;
512
513                         case SQL_IN_E_QUOTE:
514
515                                 /*
516                                  * Eventually we will need to handle '' specially, because
517                                  * after E'...''... we should still be in E_QUOTE state.
518                                  *
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
521                                  * literals??
522                                  */
523                                 if (*qry == '\'' && !AH->sqlparse.backSlash)
524                                         AH->sqlparse.state = SQL_SCAN;
525                                 else if (*qry == '\\')
526                                         AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
527                                 else
528                                         AH->sqlparse.backSlash = false;
529                                 break;
530
531                         case SQL_IN_DOUBLE_QUOTE:
532                                 /* We needn't handle "" specially */
533                                 if (*qry == '"')
534                                         AH->sqlparse.state = SQL_SCAN;
535                                 break;
536
537                         case SQL_IN_DOLLAR_TAG:
538                                 if (*qry == '$')
539                                 {
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;
543                                 }
544                                 else if (_isDQChar(*qry, (AH->sqlparse.tagBuf->len == 1)))
545                                 {
546                                         /* Valid, so add to tag */
547                                         appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
548                                 }
549                                 else
550                                 {
551                                         /*
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.
559                                          */
560                                         AH->sqlparse.state = SQL_SCAN;
561                                         qry--;
562                                         continue;
563                                 }
564                                 break;
565
566                         case SQL_IN_DOLLAR_QUOTE:
567
568                                 /*
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$...'
574                                  */
575                                 if (*qry == '$' &&
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;
580                                 break;
581                 }
582
583                 appendPQExpBufferChar(AH->sqlBuf, *qry);
584                 AH->sqlparse.lastChar = *qry;
585         }
586
587         /*
588          * If we get here, we've processed entire bufferload with no complete SQL
589          * stmt
590          */
591         return eos;
592 }
593
594
595 /* Convenience function to send one or more queries. Monitors result to handle COPY statements */
596 int
597 ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, size_t bufLen)
598 {
599         char       *qry = (char *) qryv;
600         char       *eos = qry + bufLen;
601
602         /*
603          * fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n",
604          * qry);
605          */
606
607         /* Could switch between command and COPY IN mode at each line */
608         while (qry < eos)
609         {
610                 /*
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.
620                  */
621                 if (AH->pgCopyIn || AH->writingCopyData)
622                         qry = _sendCopyLine(AH, qry, eos);
623                 else
624                         qry = _sendSQLLine(AH, qry, eos);
625         }
626
627         return 1;
628 }
629
630 void
631 StartTransaction(ArchiveHandle *AH)
632 {
633         PQExpBuffer qry = createPQExpBuffer();
634
635         appendPQExpBuffer(qry, "BEGIN");
636
637         ExecuteSqlCommand(AH, qry, "could not start database transaction");
638
639         destroyPQExpBuffer(qry);
640 }
641
642 void
643 CommitTransaction(ArchiveHandle *AH)
644 {
645         PQExpBuffer qry = createPQExpBuffer();
646
647         appendPQExpBuffer(qry, "COMMIT");
648
649         ExecuteSqlCommand(AH, qry, "could not commit database transaction");
650
651         destroyPQExpBuffer(qry);
652 }
653
654 static bool
655 _isIdentChar(unsigned char c)
656 {
657         if ((c >= 'a' && c <= 'z')
658                 || (c >= 'A' && c <= 'Z')
659                 || (c >= '0' && c <= '9')
660                 || (c == '_')
661                 || (c == '$')
662                 || (c >= (unsigned char) '\200')                /* no need to check <= \377 */
663                 )
664                 return true;
665         else
666                 return false;
667 }
668
669 static bool
670 _isDQChar(unsigned char c, bool atStart)
671 {
672         if ((c >= 'a' && c <= 'z')
673                 || (c >= 'A' && c <= 'Z')
674                 || (c == '_')
675                 || (!atStart && c >= '0' && c <= '9')
676                 || (c >= (unsigned char) '\200')                /* no need to check <= \377 */
677                 )
678                 return true;
679         else
680                 return false;
681 }