]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_dumpall.c
Translation updates, some messages tweaked.
[postgresql] / src / bin / pg_dump / pg_dumpall.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_dumpall
4  *
5  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.8 2002/09/22 20:57:20 petere Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres_fe.h"
15
16 #include <unistd.h>
17 #ifdef ENABLE_NLS
18 #include <locale.h>
19 #endif
20 #ifdef HAVE_GETOPT_H
21 #include <getopt.h>
22 #endif
23 #ifndef HAVE_STRDUP
24 #include "strdup.h"
25 #endif
26 #include <errno.h>
27
28 #include "dumputils.h"
29 #include "libpq-fe.h"
30 #include "pg_backup.h"
31 #include "pqexpbuffer.h"
32
33 #define _(x) gettext((x))
34
35
36 static char *progname;
37
38 static void help(void);
39
40 static void dumpUsers(PGconn *conn);
41 static void dumpGroups(PGconn *conn);
42 static void dumpCreateDB(PGconn *conn);
43 static void dumpDatabaseConfig(PGconn *conn, const char *dbname);
44 static void dumpUserConfig(PGconn *conn, const char *username);
45 static void makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name);
46 static void dumpDatabases(PGconn *conn);
47
48 static int      runPgDump(const char *dbname);
49 static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
50                                 const char *pguser, bool require_password);
51 static PGresult *executeQuery(PGconn *conn, const char *query);
52 static char *findPgDump(const char *argv0);
53
54
55 char       *pgdumploc;
56 PQExpBuffer pgdumpopts;
57 bool            output_clean = false;
58 bool            verbose = false;
59 int                     server_version;
60
61
62
63 int
64 main(int argc, char *argv[])
65 {
66         char       *pghost = NULL;
67         char       *pgport = NULL;
68         char       *pguser = NULL;
69         bool            force_password = false;
70         bool            globals_only = false;
71         PGconn     *conn;
72         int                     c;
73
74 #ifdef HAVE_GETOPT_LONG
75         static struct option long_options[] = {
76                 {"clean", no_argument, NULL, 'c'},
77                 {"inserts", no_argument, NULL, 'd'},
78                 {"attribute-inserts", no_argument, NULL, 'D'},
79                 {"column-inserts", no_argument, NULL, 'D'},
80                 {"host", required_argument, NULL, 'h'},
81                 {"ignore-version", no_argument, NULL, 'i'},
82                 {"oids", no_argument, NULL, 'o'},
83                 {"port", required_argument, NULL, 'p'},
84                 {"password", no_argument, NULL, 'W'},
85                 {"username", required_argument, NULL, 'U'},
86                 {"verbose", no_argument, NULL, 'v'},
87                 {NULL, 0, NULL, 0}
88         };
89
90         int                     optindex;
91 #endif
92
93 #ifdef ENABLE_NLS
94         setlocale(LC_ALL, "");
95         bindtextdomain("pg_dump", LOCALEDIR);
96         textdomain("pg_dump");
97 #endif
98
99         if (!strrchr(argv[0], '/'))
100                 progname = argv[0];
101         else
102                 progname = strrchr(argv[0], '/') + 1;
103
104         if (argc > 1)
105         {
106                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
107                 {
108                         help();
109                         exit(0);
110                 }
111                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
112                 {
113                         puts("pg_dumpall (PostgreSQL) " PG_VERSION);
114                         exit(0);
115                 }
116         }
117
118         pgdumploc = findPgDump(argv[0]);
119         pgdumpopts = createPQExpBuffer();
120
121 #ifdef HAVE_GETOPT_LONG
122         while ((c = getopt_long(argc, argv, "cdDgh:iop:U:vW", long_options, &optindex)) != -1)
123 #else
124         while ((c = getopt(argc, argv, "cdDgh:iop:U:vW")) != -1)
125 #endif
126         {
127                 switch (c)
128                 {
129                         case 'c':
130                                 output_clean = true;
131                                 break;
132
133                         case 'd':
134                         case 'D':
135                                 appendPQExpBuffer(pgdumpopts, " -%c", c);
136                                 break;
137
138                         case 'g':
139                                 globals_only = true;
140                                 break;
141
142                         case 'h':
143                                 pghost = optarg;
144                                 appendPQExpBuffer(pgdumpopts, " -h '%s'", pghost);
145                                 break;
146
147                         case 'i':
148                         case 'o':
149                                 appendPQExpBuffer(pgdumpopts, " -%c", c);
150                                 break;
151
152                         case 'p':
153                                 pgport = optarg;
154                                 appendPQExpBuffer(pgdumpopts, " -p '%s'", pgport);
155                                 break;
156
157                         case 'U':
158                                 pguser = optarg;
159                                 appendPQExpBuffer(pgdumpopts, " -U '%s'", pguser);
160                                 break;
161
162                         case 'v':
163                                 verbose = true;
164                                 appendPQExpBuffer(pgdumpopts, " -v");
165                                 break;
166
167                         case 'W':
168                                 force_password = true;
169                                 appendPQExpBuffer(pgdumpopts, " -W");
170                                 break;
171
172                         default:
173                                 fprintf(stderr, _("Try '%s --help' for more information.\n"), progname);
174                                 exit(1);
175                 }
176         }
177
178         if (optind < argc)
179         {
180                 fprintf(stderr,
181                                 _("%s: too many command line options (first is '%s')\n"
182                                   "Try '%s --help' for more information.\n"),
183                                 progname, argv[optind], progname);
184                 exit(1);
185         }
186
187
188         conn = connectDatabase("template1", pghost, pgport, pguser, force_password);
189
190         printf("--\n");
191         printf("-- PostgreSQL database cluster dump\n");
192         printf("--\n\n");
193         printf("\\connect \"template1\"\n\n");
194
195         dumpUsers(conn);
196         dumpGroups(conn);
197
198         if (globals_only)
199                 goto end;
200
201         dumpCreateDB(conn);
202         dumpDatabases(conn);
203
204 end:
205         PQfinish(conn);
206         exit(0);
207 }
208
209
210
211 static void
212 help(void)
213 {
214         printf(_("%s extracts a PostgreSQL database cluster into an SQL script file.\n\n"), progname);
215         printf(_("Usage:\n"));
216         printf(_("  %s [OPTIONS]\n\n"), progname);
217
218         printf(_("Options:\n"));
219 #ifdef HAVE_GETOPT_LONG
220         printf(_("  -c, --clean              clean (drop) databases prior to create\n"));
221         printf(_("  -d, --inserts            dump data as INSERT, rather than COPY, commands\n"));
222         printf(_("  -D, --column-inserts     dump data as INSERT commands with column names\n"));
223         printf(_("  -g, --globals-only       dump only global objects, no databases\n"));
224         printf(_("  -h, --host=HOSTNAME      database server host name\n"));
225         printf(_("  -i, --ignore-version     proceed even when server version mismatches\n"
226                          "                           pg_dumpall version\n"));
227         printf(_("  -o, --oids               include OIDs in dump\n"));
228         printf(_("  -p, --port=PORT          database server port number\n"));
229         printf(_("  -U, --username=NAME      connect as specified database user\n"));
230         printf(_("  -v, --verbose            verbose mode\n"));
231         printf(_("  -W, --password           force password prompt (should happen automatically)\n"));
232 #else                                                   /* not HAVE_GETOPT_LONG */
233         printf(_("  -c                       clean (drop) databases prior to create\n"));
234         printf(_("  -d                       dump data as INSERT, rather than COPY, commands\n"));
235         printf(_("  -D                       dump data as INSERT commands with column names\n"));
236         printf(_("  -g                       dump only global objects, no databases\n"));
237         printf(_("  -h HOSTNAME              database server host name\n"));
238         printf(_("  -i                       proceed even when server version mismatches\n"
239                          "                           pg_dumpall version\n"));
240         printf(_("  -o                       include OIDs in dump\n"));
241         printf(_("  -p PORT                  database server port number\n"));
242         printf(_("  -U NAME                  connect as specified database user\n"));
243         printf(_("  -v                       verbose mode\n"));
244         printf(_("  -W                       force password prompt (should happen automatically)\n"));
245 #endif   /* not HAVE_GETOPT_LONG */
246
247         printf(_("\nThe SQL script will be written to the standard output.\n\n"));
248         printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
249 }
250
251
252
253 /*
254  * Dump users (but not the user created by initdb).
255  */
256 static void
257 dumpUsers(PGconn *conn)
258 {
259         PGresult   *res;
260         int                     i;
261
262         printf("--\n-- Users\n--\n\n");
263         printf("DELETE FROM pg_shadow WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');\n\n");
264
265         res = executeQuery(conn,
266                                            "SELECT usename, usesysid, passwd, usecreatedb, usesuper, CAST(valuntil AS timestamp) "
267                                            "FROM pg_shadow "
268                                            "WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');");
269
270         for (i = 0; i < PQntuples(res); i++)
271         {
272                 PQExpBuffer buf = createPQExpBuffer();
273                 const char *username;
274
275                 username = PQgetvalue(res, i, 0);
276                 appendPQExpBuffer(buf, "CREATE USER %s WITH SYSID %s",
277                                                   fmtId(username),
278                                                   PQgetvalue(res, i, 1));
279
280                 if (!PQgetisnull(res, i, 2))
281                 {
282                         appendPQExpBuffer(buf, " PASSWORD ");
283                         appendStringLiteral(buf, PQgetvalue(res, i, 2), true);
284                 }
285
286                 if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
287                         appendPQExpBuffer(buf, " CREATEDB");
288                 else
289                         appendPQExpBuffer(buf, " NOCREATEDB");
290
291                 if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
292                         appendPQExpBuffer(buf, " CREATEUSER");
293                 else
294                         appendPQExpBuffer(buf, " NOCREATEUSER");
295
296                 if (!PQgetisnull(res, i, 5))
297                         appendPQExpBuffer(buf, " VALID UNTIL '%s'", PQgetvalue(res, i, 5));
298
299                 appendPQExpBuffer(buf, ";\n");
300
301                 printf("%s", buf->data);
302                 destroyPQExpBuffer(buf);
303
304                 if (server_version >= 70300)
305                         dumpUserConfig(conn, username);
306         }
307
308         PQclear(res);
309         printf("\n\n");
310 }
311
312
313
314 /*
315  * Dump groups.
316  */
317 static void
318 dumpGroups(PGconn *conn)
319 {
320         PGresult   *res;
321         int                     i;
322
323         printf("--\n-- Groups\n--\n\n");
324         printf("DELETE FROM pg_group;\n\n");
325
326         res = executeQuery(conn, "SELECT groname, grosysid, grolist FROM pg_group;");
327
328         for (i = 0; i < PQntuples(res); i++)
329         {
330                 PQExpBuffer buf = createPQExpBuffer();
331                 char       *val;
332                 char       *tok;
333
334                 appendPQExpBuffer(buf, "CREATE GROUP %s WITH SYSID %s;\n",
335                                                   fmtId(PQgetvalue(res, i, 0)),
336                                                   PQgetvalue(res, i, 1));
337
338                 val = strdup(PQgetvalue(res, i, 2));
339                 tok = strtok(val, ",{}");
340                 do
341                 {
342                         PGresult   *res2;
343                         PQExpBuffer buf2 = createPQExpBuffer();
344                         int                     j;
345
346                         appendPQExpBuffer(buf2, "SELECT usename FROM pg_shadow WHERE usesysid = %s;", tok);
347                         res2 = executeQuery(conn, buf2->data);
348                         destroyPQExpBuffer(buf2);
349
350                         for (j = 0; j < PQntuples(res2); j++)
351                         {
352                                 appendPQExpBuffer(buf, "ALTER GROUP %s ", fmtId(PQgetvalue(res, i, 0)));
353                                 appendPQExpBuffer(buf, "ADD USER %s;\n", fmtId(PQgetvalue(res2, j, 0)));
354                         }
355
356                         PQclear(res2);
357
358                         tok = strtok(NULL, "{},");
359                 }
360                 while (tok);
361
362                 printf("%s", buf->data);
363                 destroyPQExpBuffer(buf);
364         }
365
366         PQclear(res);
367         printf("\n\n");
368 }
369
370
371
372 /*
373  * Dump commands to create each database.
374  *
375  * To minimize the number of reconnections (and possibly ensuing
376  * password prompts) required by the output script, we emit all CREATE
377  * DATABASE commands during the initial phase of the script, and then
378  * run pg_dump for each database to dump the contents of that
379  * database.  We skip databases marked not datallowconn, since we'd be
380  * unable to connect to them anyway (and besides, we don't want to
381  * dump template0).
382  */
383 static void
384 dumpCreateDB(PGconn *conn)
385 {
386         PGresult   *res;
387         int                     i;
388
389         printf("--\n-- Database creation\n--\n\n");
390
391         /*
392          * Basically this query returns: dbname, dbowner, encoding,
393          * istemplate, dbpath
394          */
395         res = executeQuery(conn, "SELECT datname, coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), pg_encoding_to_char(d.encoding), datistemplate, datpath FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) WHERE datallowconn ORDER BY 1;");
396
397         for (i = 0; i < PQntuples(res); i++)
398         {
399                 PQExpBuffer buf = createPQExpBuffer();
400                 char       *dbname = PQgetvalue(res, i, 0);
401                 char       *dbowner = PQgetvalue(res, i, 1);
402                 char       *dbencoding = PQgetvalue(res, i, 2);
403                 char       *dbistemplate = PQgetvalue(res, i, 3);
404                 char       *dbpath = PQgetvalue(res, i, 4);
405
406                 if (strcmp(dbname, "template1") == 0)
407                         continue;
408
409                 if (output_clean)
410                         appendPQExpBuffer(buf, "DROP DATABASE %s\n;", fmtId(dbname));
411
412                 appendPQExpBuffer(buf, "CREATE DATABASE %s", fmtId(dbname));
413                 appendPQExpBuffer(buf, " WITH OWNER = %s TEMPLATE = template0", fmtId(dbowner));
414
415                 if (strcmp(dbpath, "") != 0)
416                 {
417                         appendPQExpBuffer(buf, " LOCATION = ");
418                         appendStringLiteral(buf, dbpath, true);
419                 }
420
421                 appendPQExpBuffer(buf, " ENCODING = ");
422                 appendStringLiteral(buf, dbencoding, true);
423
424                 appendPQExpBuffer(buf, ";\n");
425
426                 if (strcmp(dbistemplate, "t") == 0)
427                 {
428                         appendPQExpBuffer(buf, "UPDATE pg_database SET datistemplate = 't' WHERE datname = ");
429                         appendStringLiteral(buf, dbname, true);
430                         appendPQExpBuffer(buf, ";\n");
431                 }
432                 printf("%s", buf->data);
433                 destroyPQExpBuffer(buf);
434
435                 if (server_version >= 70300)
436                         dumpDatabaseConfig(conn, dbname);
437         }
438
439         PQclear(res);
440         printf("\n\n");
441 }
442
443
444
445 /*
446  * Dump database-specific configuration
447  */
448 static void
449 dumpDatabaseConfig(PGconn *conn, const char *dbname)
450 {
451         PQExpBuffer buf = createPQExpBuffer();
452         int                     count = 1;
453
454         for (;;)
455         {
456                 PGresult   *res;
457
458                 printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE datname = ", count);
459                 appendStringLiteral(buf, dbname, true);
460                 appendPQExpBuffer(buf, ";");
461
462                 res = executeQuery(conn, buf->data);
463                 if (!PQgetisnull(res, 0, 0))
464                 {
465                         makeAlterConfigCommand(PQgetvalue(res, 0, 0), "DATABASE", dbname);
466                         PQclear(res);
467                         count++;
468                 }
469                 else
470                 {
471                         PQclear(res);
472                         break;
473                 }
474         }
475
476         destroyPQExpBuffer(buf);
477 }
478
479
480
481 /*
482  * Dump user-specific configuration
483  */
484 static void
485 dumpUserConfig(PGconn *conn, const char *username)
486 {
487         PQExpBuffer buf = createPQExpBuffer();
488         int                     count = 1;
489
490         for (;;)
491         {
492                 PGresult   *res;
493
494                 printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = ", count);
495                 appendStringLiteral(buf, username, true);
496                 appendPQExpBuffer(buf, ";");
497
498                 res = executeQuery(conn, buf->data);
499                 if (!PQgetisnull(res, 0, 0))
500                 {
501                         makeAlterConfigCommand(PQgetvalue(res, 0, 0), "USER", username);
502                         PQclear(res);
503                         count++;
504                 }
505                 else
506                 {
507                         PQclear(res);
508                         break;
509                 }
510         }
511
512         destroyPQExpBuffer(buf);
513 }
514
515
516
517 /*
518  * Helper function for dumpXXXConfig().
519  */
520 static void
521 makeAlterConfigCommand(const char *arrayitem, const char *type, const char *name)
522 {
523         char       *pos;
524         char       *mine;
525         PQExpBuffer buf = createPQExpBuffer();
526
527         mine = strdup(arrayitem);
528         pos = strchr(mine, '=');
529         if (pos == NULL)
530                 return;
531
532         *pos = 0;
533         appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
534         appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
535         appendStringLiteral(buf, pos + 1, false);
536         appendPQExpBuffer(buf, ";\n");
537
538         printf("%s", buf->data);
539         destroyPQExpBuffer(buf);
540         free(mine);
541 }
542
543
544
545 /*
546  * Dump contents of databases.
547  */
548 static void
549 dumpDatabases(PGconn *conn)
550 {
551         PGresult   *res;
552         int                     i;
553
554         res = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;");
555         for (i = 0; i < PQntuples(res); i++)
556         {
557                 int                     ret;
558
559                 char       *dbname = PQgetvalue(res, i, 0);
560
561                 if (verbose)
562                         fprintf(stderr, _("%s: dumping database \"%s\"...\n"), progname, dbname);
563
564                 printf("\\connect %s\n", fmtId(dbname));
565                 ret = runPgDump(dbname);
566                 if (ret != 0)
567                 {
568                         fprintf(stderr, _("%s: pg_dump failed on %s, exiting\n"), progname, dbname);
569                         exit(1);
570                 }
571         }
572
573         PQclear(res);
574 }
575
576
577
578 /*
579  * Run pg_dump on dbname.
580  */
581 static int
582 runPgDump(const char *dbname)
583 {
584         PQExpBuffer cmd = createPQExpBuffer();
585         int                     ret;
586
587         appendPQExpBuffer(cmd, "%s %s -X use-set-session-authorization -Fp %s",
588                                           pgdumploc, pgdumpopts->data, dbname);
589         if (verbose)
590                 fprintf(stderr, _("%s: running %s\n"), progname, cmd->data);
591
592         fflush(stdout);
593         fflush(stderr);
594
595         ret = system(cmd->data);
596         destroyPQExpBuffer(cmd);
597
598         return ret;
599 }
600
601
602
603 /*
604  * Make a database connection with the given parameters.  An
605  * interactive password prompt is automatically issued if required.
606  */
607 static PGconn *
608 connectDatabase(const char *dbname, const char *pghost, const char *pgport,
609                                 const char *pguser, bool require_password)
610 {
611         PGconn     *conn;
612         char       *password = NULL;
613         bool            need_pass = false;
614         PGresult   *res;
615
616         if (require_password)
617                 password = simple_prompt("Password: ", 100, false);
618
619         /*
620          * Start the connection.  Loop until we have a password if requested
621          * by backend.
622          */
623         do
624         {
625                 need_pass = false;
626                 conn = PQsetdbLogin(pghost, pgport, NULL, NULL, dbname, pguser, password);
627
628                 if (!conn)
629                 {
630                         fprintf(stderr, _("%s: could not connect to database %s\n"),
631                                         progname, dbname);
632                         exit(1);
633                 }
634
635                 if (PQstatus(conn) == CONNECTION_BAD &&
636                         strcmp(PQerrorMessage(conn), "fe_sendauth: no password supplied\n") == 0 &&
637                         !feof(stdin))
638                 {
639                         PQfinish(conn);
640                         need_pass = true;
641                         free(password);
642                         password = NULL;
643                         password = simple_prompt("Password: ", 100, false);
644                 }
645         } while (need_pass);
646
647         if (password)
648                 free(password);
649
650         /* check to see that the backend connection was successfully made */
651         if (PQstatus(conn) == CONNECTION_BAD)
652         {
653                 fprintf(stderr, _("%s: could not connect to database %s: %s\n"),
654                                 progname, dbname, PQerrorMessage(conn));
655                 exit(1);
656         }
657
658         res = executeQuery(conn, "SELECT version();");
659         if (PQntuples(res) != 1)
660         {
661                 fprintf(stderr, _("%s: could not get server version\n"), progname);
662                 exit(1);
663         }
664         else
665         {
666                 char *val = PQgetvalue(res, 0, 0);
667                 server_version = parse_version(val + strcspn(val, "0123456789"));
668                 if (server_version < 0)
669                 {
670                         fprintf(stderr, _("%s: could not parse server version \"%s\"\n"), progname, val);
671                         exit(1);
672                 }
673         }
674
675         return conn;
676 }
677
678
679
680 /*
681  * Run a query, return the results, exit program on failure.
682  */
683 static PGresult *
684 executeQuery(PGconn *conn, const char *query)
685 {
686         PGresult   *res;
687
688         res = PQexec(conn, query);
689         if (!res ||
690                 PQresultStatus(res) != PGRES_TUPLES_OK)
691         {
692                 fprintf(stderr, _("%s: query failed: %s"), progname, PQerrorMessage(conn));
693                 fprintf(stderr, _("%s: query was: %s\n"), progname, query);
694                 PQfinish(conn);
695                 exit(1);
696         }
697
698         return res;
699 }
700
701
702
703 /*
704  * Find location of pg_dump executable.
705  */
706 static char *
707 findPgDump(const char *argv0)
708 {
709         char       *last;
710         PQExpBuffer cmd;
711         static char *result = NULL;
712
713         if (result)
714                 return result;
715
716         cmd = createPQExpBuffer();
717         last = strrchr(argv0, '/');
718
719         if (!last)
720                 appendPQExpBuffer(cmd, "pg_dump");
721         else
722         {
723                 char       *dir = strdup(argv0);
724
725                 *(dir + (last - argv0)) = '\0';
726                 appendPQExpBuffer(cmd, "%s/pg_dump", dir);
727         }
728
729         result = strdup(cmd->data);
730
731         appendPQExpBuffer(cmd, " -V >/dev/null 2>&1");
732         if (system(cmd->data) == 0)
733                 goto end;
734
735         result = BINDIR "/pg_dump";
736         if (system(BINDIR "/pg_dump -V >/dev/null 2>&1") == 0)
737                 goto end;
738
739         fprintf(stderr, _("%s: could not find pg_dump\n"
740                 "Make sure it is in the path or in the same directory as %s.\n"),
741                         progname, progname);
742         exit(1);
743
744 end:
745         destroyPQExpBuffer(cmd);
746         return result;
747 }