Disallow empty passwords in LDAP authentication, the same way
[PostgreSQL.git] / src / bin / pg_dump / pg_backup_db.c
blob499d2c115e31bcb3cf1f54571e16f88e0c68bd7c
1 /*-------------------------------------------------------------------------
3 * pg_backup_db.c
5 * Implements the basic DB functions used by the archiver.
7 * IDENTIFICATION
8 * $PostgreSQL$
10 *-------------------------------------------------------------------------
13 #include "pg_backup_db.h"
14 #include "dumputils.h"
16 #include <unistd.h>
18 #include <ctype.h>
20 #ifdef HAVE_TERMIOS_H
21 #include <termios.h>
22 #endif
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
38 static int
39 _parse_version(ArchiveHandle *AH, const char *versionString)
41 int v;
43 v = parse_version(versionString);
44 if (v < 0)
45 die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
47 return v;
50 static void
51 _check_database_version(ArchiveHandle *AH)
53 int myversion;
54 const char *remoteversion_str;
55 int remoteversion;
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.
87 int
88 ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
90 PGconn *newConn;
91 const char *newdbname;
92 const char *newusername;
94 if (!dbname)
95 newdbname = PQdb(AH->connection);
96 else
97 newdbname = dbname;
99 if (!username)
100 newusername = PQuser(AH->connection);
101 else
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)
107 return 1;
109 newConn = _connectDB(AH, newdbname, newusername);
111 PQfinish(AH->connection);
112 AH->connection = newConn;
114 return 1;
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
124 * start of the run.
126 static PGconn *
127 _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
129 PGconn *newConn;
130 const char *newdb;
131 const char *newuser;
132 char *password = AH->savedPassword;
133 bool new_pass;
135 if (!reqdb)
136 newdb = PQdb(AH->connection);
137 else
138 newdb = reqdb;
140 if (!requser || strlen(requser) == 0)
141 newuser = PQuser(AH->connection);
142 else
143 newuser = requser;
145 ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n",
146 newdb, newuser);
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");
157 new_pass = false;
158 newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
159 NULL, NULL, newdb,
160 newuser, password);
161 if (!newConn)
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));
169 PQfinish(newConn);
171 if (password)
172 fprintf(stderr, "Password incorrect\n");
174 fprintf(stderr, "Connecting to %s as %s\n",
175 newdb, newuser);
177 if (password)
178 free(password);
180 if (AH->promptPassword != TRI_NO)
181 password = simple_prompt("Password: ", 100, false);
182 else
183 die_horribly(AH, modulename, "connection needs password\n");
185 if (password == NULL)
186 die_horribly(AH, modulename, "out of memory\n");
187 new_pass = true;
189 } while (new_pass);
191 AH->savedPassword = password;
193 /* check for version mismatch */
194 _check_database_version(AH);
196 PQsetNoticeProcessor(newConn, notice_processor, NULL);
198 return newConn;
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.
211 PGconn *
212 ConnectDatabase(Archive *AHX,
213 const char *dbname,
214 const char *pghost,
215 const char *pgport,
216 const char *username,
217 enum trivalue prompt_password)
219 ArchiveHandle *AH = (ArchiveHandle *) AHX;
220 char *password = AH->savedPassword;
221 bool new_pass;
223 if (AH->connection)
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
236 * backend.
240 new_pass = false;
241 AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
242 dbname, username, password);
244 if (!AH->connection)
245 die_horribly(AH, modulename, "failed to connect to database\n");
247 if (PQstatus(AH->connection) == CONNECTION_BAD &&
248 PQconnectionNeedsPassword(AH->connection) &&
249 password == NULL &&
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");
256 new_pass = true;
258 } while (new_pass);
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;
276 static void
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 */
285 static void
286 ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
288 PGconn *conn = AH->connection;
289 PGresult *res;
290 char errStmt[DB_MAX_ERR_STMT];
292 #ifdef NOT_USED
293 fprintf(stderr, "Executing: '%s'\n\n", qry);
294 #endif
295 res = PQexec(conn, qry);
297 switch (PQresultStatus(res))
299 case PGRES_COMMAND_OK:
300 case PGRES_TUPLES_OK:
301 /* A-OK */
302 break;
303 case PGRES_COPY_IN:
304 /* Assume this is an expected result */
305 AH->pgCopyIn = true;
306 break;
307 default:
308 /* trouble */
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);
319 break;
322 PQclear(res);
326 * Used by ExecuteSqlCommandBuf to send one buffered line when running a COPY command.
328 static char *
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 */
334 int isEnd = 0;
336 /* loop to find unquoted newline ending the line of COPY data */
337 for (;;)
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));
345 return eos;
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 */
354 sPos = loc;
355 while (sPos > 0 && qry[sPos - 1] == '\\')
356 sPos--;
358 sPos = loc - sPos;
361 * If an odd number of preceding slashes, then \n was escaped so set
362 * the next search pos, and loop (if any left).
364 if ((sPos & 1) == 1)
366 /* fprintf(stderr, "cr was escaped\n"); */
367 pos = loc + 1;
368 if (pos >= (eos - qry))
370 appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
371 return eos;
374 else
375 break;
378 /* We found an unquoted newline */
379 qry[loc] = '\0';
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.
388 if (AH->pgCopyIn &&
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)
398 PGresult *res;
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));
409 PQclear(res);
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).
421 static char *
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
444 * the buffer.
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.
456 qry++;
457 while (qry < eos && *qry == '\n')
458 qry++;
460 /* We've finished one line, so exit */
461 return qry;
463 else if (*qry == '\'')
465 if (AH->sqlparse.lastChar == 'E')
466 AH->sqlparse.state = SQL_IN_E_QUOTE;
467 else
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();
487 else
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--;
499 break;
501 case SQL_IN_SQL_COMMENT:
502 if (*qry == '\n')
503 AH->sqlparse.state = SQL_SCAN;
504 break;
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;
514 break;
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;
522 else
523 AH->sqlparse.backSlash = false;
524 break;
526 case SQL_IN_E_QUOTE:
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
534 * literals??
536 if (*qry == '\'' && !AH->sqlparse.backSlash)
537 AH->sqlparse.state = SQL_SCAN;
538 else if (*qry == '\\')
539 AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
540 else
541 AH->sqlparse.backSlash = false;
542 break;
544 case SQL_IN_DOUBLE_QUOTE:
545 /* We needn't handle "" specially */
546 if (*qry == '"')
547 AH->sqlparse.state = SQL_SCAN;
548 break;
550 case SQL_IN_DOLLAR_TAG:
551 if (*qry == '$')
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);
562 else
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;
574 qry--;
575 continue;
577 break;
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$...'
588 if (*qry == '$' &&
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;
593 break;
596 appendPQExpBufferChar(AH->sqlBuf, *qry);
597 AH->sqlparse.lastChar = *qry;
601 * If we get here, we've processed entire bufferload with no complete SQL
602 * stmt
604 return eos;
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",
617 * qry);
620 /* Could switch between command and COPY IN mode at each line */
621 while (qry < eos)
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);
636 else
637 qry = _sendSQLLine(AH, qry, eos);
640 return 1;
643 void
644 StartTransaction(ArchiveHandle *AH)
646 ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
649 void
650 CommitTransaction(ArchiveHandle *AH)
652 ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
655 static bool
656 _isIdentChar(unsigned char c)
658 if ((c >= 'a' && c <= 'z')
659 || (c >= 'A' && c <= 'Z')
660 || (c >= '0' && c <= '9')
661 || (c == '_')
662 || (c == '$')
663 || (c >= (unsigned char) '\200') /* no need to check <= \377 */
665 return true;
666 else
667 return false;
670 static bool
671 _isDQChar(unsigned char c, bool atStart)
673 if ((c >= 'a' && c <= 'z')
674 || (c >= 'A' && c <= 'Z')
675 || (c == '_')
676 || (!atStart && c >= '0' && c <= '9')
677 || (c >= (unsigned char) '\200') /* no need to check <= \377 */
679 return true;
680 else
681 return false;