1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_dumpall.c |
4 | * |
5 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
6 | * Portions Copyright (c) 1994, Regents of the University of California |
7 | * |
8 | * pg_dumpall forces all pg_dump output to be text, since it also outputs |
9 | * text into the same output stream. |
10 | * |
11 | * src/bin/pg_dump/pg_dumpall.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | |
16 | #include "postgres_fe.h" |
17 | |
18 | #include <time.h> |
19 | #include <unistd.h> |
20 | |
21 | #include "getopt_long.h" |
22 | |
23 | #include "dumputils.h" |
24 | #include "pg_backup.h" |
25 | #include "common/file_utils.h" |
26 | #include "common/logging.h" |
27 | #include "fe_utils/connect.h" |
28 | #include "fe_utils/string_utils.h" |
29 | |
30 | /* version string we expect back from pg_dump */ |
31 | #define PGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n" |
32 | |
33 | |
34 | static void help(void); |
35 | |
36 | static void dropRoles(PGconn *conn); |
37 | static void dumpRoles(PGconn *conn); |
38 | static void dumpRoleMembership(PGconn *conn); |
39 | static void dumpGroups(PGconn *conn); |
40 | static void dropTablespaces(PGconn *conn); |
41 | static void dumpTablespaces(PGconn *conn); |
42 | static void dropDBs(PGconn *conn); |
43 | static void dumpUserConfig(PGconn *conn, const char *username); |
44 | static void dumpDatabases(PGconn *conn); |
45 | static void dumpTimestamp(const char *msg); |
46 | static int runPgDump(const char *dbname, const char *create_opts); |
47 | static void buildShSecLabels(PGconn *conn, |
48 | const char *catalog_name, Oid objectId, |
49 | const char *objtype, const char *objname, |
50 | PQExpBuffer buffer); |
51 | static PGconn *connectDatabase(const char *dbname, const char *connstr, const char *pghost, const char *pgport, |
52 | const char *pguser, trivalue prompt_password, bool fail_on_error); |
53 | static char *constructConnStr(const char **keywords, const char **values); |
54 | static PGresult *executeQuery(PGconn *conn, const char *query); |
55 | static void executeCommand(PGconn *conn, const char *query); |
56 | static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns, |
57 | SimpleStringList *names); |
58 | |
59 | static char pg_dump_bin[MAXPGPATH]; |
60 | static const char *progname; |
61 | static PQExpBuffer pgdumpopts; |
62 | static char *connstr = "" ; |
63 | static bool output_clean = false; |
64 | static bool skip_acls = false; |
65 | static bool verbose = false; |
66 | static bool dosync = true; |
67 | |
68 | static int binary_upgrade = 0; |
69 | static int column_inserts = 0; |
70 | static int disable_dollar_quoting = 0; |
71 | static int disable_triggers = 0; |
72 | static int if_exists = 0; |
73 | static int inserts = 0; |
74 | static int no_tablespaces = 0; |
75 | static int use_setsessauth = 0; |
76 | static int = 0; |
77 | static int no_publications = 0; |
78 | static int no_security_labels = 0; |
79 | static int no_subscriptions = 0; |
80 | static int no_unlogged_table_data = 0; |
81 | static int no_role_passwords = 0; |
82 | static int server_version; |
83 | static int load_via_partition_root = 0; |
84 | static int on_conflict_do_nothing = 0; |
85 | |
86 | static char role_catalog[10]; |
87 | #define PG_AUTHID "pg_authid" |
88 | #define PG_ROLES "pg_roles " |
89 | |
90 | static FILE *OPF; |
91 | static char *filename = NULL; |
92 | |
93 | static SimpleStringList database_exclude_patterns = {NULL, NULL}; |
94 | static SimpleStringList database_exclude_names = {NULL, NULL}; |
95 | |
96 | #define exit_nicely(code) exit(code) |
97 | |
98 | int |
99 | main(int argc, char *argv[]) |
100 | { |
101 | static struct option long_options[] = { |
102 | {"data-only" , no_argument, NULL, 'a'}, |
103 | {"clean" , no_argument, NULL, 'c'}, |
104 | {"encoding" , required_argument, NULL, 'E'}, |
105 | {"file" , required_argument, NULL, 'f'}, |
106 | {"globals-only" , no_argument, NULL, 'g'}, |
107 | {"host" , required_argument, NULL, 'h'}, |
108 | {"dbname" , required_argument, NULL, 'd'}, |
109 | {"database" , required_argument, NULL, 'l'}, |
110 | {"no-owner" , no_argument, NULL, 'O'}, |
111 | {"port" , required_argument, NULL, 'p'}, |
112 | {"roles-only" , no_argument, NULL, 'r'}, |
113 | {"schema-only" , no_argument, NULL, 's'}, |
114 | {"superuser" , required_argument, NULL, 'S'}, |
115 | {"tablespaces-only" , no_argument, NULL, 't'}, |
116 | {"username" , required_argument, NULL, 'U'}, |
117 | {"verbose" , no_argument, NULL, 'v'}, |
118 | {"no-password" , no_argument, NULL, 'w'}, |
119 | {"password" , no_argument, NULL, 'W'}, |
120 | {"no-privileges" , no_argument, NULL, 'x'}, |
121 | {"no-acl" , no_argument, NULL, 'x'}, |
122 | |
123 | /* |
124 | * the following options don't have an equivalent short option letter |
125 | */ |
126 | {"attribute-inserts" , no_argument, &column_inserts, 1}, |
127 | {"binary-upgrade" , no_argument, &binary_upgrade, 1}, |
128 | {"column-inserts" , no_argument, &column_inserts, 1}, |
129 | {"disable-dollar-quoting" , no_argument, &disable_dollar_quoting, 1}, |
130 | {"disable-triggers" , no_argument, &disable_triggers, 1}, |
131 | {"exclude-database" , required_argument, NULL, 6}, |
132 | {"extra-float-digits" , required_argument, NULL, 5}, |
133 | {"if-exists" , no_argument, &if_exists, 1}, |
134 | {"inserts" , no_argument, &inserts, 1}, |
135 | {"lock-wait-timeout" , required_argument, NULL, 2}, |
136 | {"no-tablespaces" , no_argument, &no_tablespaces, 1}, |
137 | {"quote-all-identifiers" , no_argument, "e_all_identifiers, 1}, |
138 | {"load-via-partition-root" , no_argument, &load_via_partition_root, 1}, |
139 | {"role" , required_argument, NULL, 3}, |
140 | {"use-set-session-authorization" , no_argument, &use_setsessauth, 1}, |
141 | {"no-comments" , no_argument, &no_comments, 1}, |
142 | {"no-publications" , no_argument, &no_publications, 1}, |
143 | {"no-role-passwords" , no_argument, &no_role_passwords, 1}, |
144 | {"no-security-labels" , no_argument, &no_security_labels, 1}, |
145 | {"no-subscriptions" , no_argument, &no_subscriptions, 1}, |
146 | {"no-sync" , no_argument, NULL, 4}, |
147 | {"no-unlogged-table-data" , no_argument, &no_unlogged_table_data, 1}, |
148 | {"on-conflict-do-nothing" , no_argument, &on_conflict_do_nothing, 1}, |
149 | {"rows-per-insert" , required_argument, NULL, 7}, |
150 | |
151 | {NULL, 0, NULL, 0} |
152 | }; |
153 | |
154 | char *pghost = NULL; |
155 | char *pgport = NULL; |
156 | char *pguser = NULL; |
157 | char *pgdb = NULL; |
158 | char *use_role = NULL; |
159 | const char *dumpencoding = NULL; |
160 | trivalue prompt_password = TRI_DEFAULT; |
161 | bool data_only = false; |
162 | bool globals_only = false; |
163 | bool roles_only = false; |
164 | bool tablespaces_only = false; |
165 | PGconn *conn; |
166 | int encoding; |
167 | const char *std_strings; |
168 | int c, |
169 | ret; |
170 | int optindex; |
171 | |
172 | pg_logging_init(argv[0]); |
173 | pg_logging_set_level(PG_LOG_WARNING); |
174 | set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump" )); |
175 | progname = get_progname(argv[0]); |
176 | |
177 | if (argc > 1) |
178 | { |
179 | if (strcmp(argv[1], "--help" ) == 0 || strcmp(argv[1], "-?" ) == 0) |
180 | { |
181 | help(); |
182 | exit_nicely(0); |
183 | } |
184 | if (strcmp(argv[1], "--version" ) == 0 || strcmp(argv[1], "-V" ) == 0) |
185 | { |
186 | puts("pg_dumpall (PostgreSQL) " PG_VERSION); |
187 | exit_nicely(0); |
188 | } |
189 | } |
190 | |
191 | if ((ret = find_other_exec(argv[0], "pg_dump" , PGDUMP_VERSIONSTR, |
192 | pg_dump_bin)) < 0) |
193 | { |
194 | char full_path[MAXPGPATH]; |
195 | |
196 | if (find_my_exec(argv[0], full_path) < 0) |
197 | strlcpy(full_path, progname, sizeof(full_path)); |
198 | |
199 | if (ret == -1) |
200 | pg_log_error("The program \"pg_dump\" is needed by %s but was not found in the\n" |
201 | "same directory as \"%s\".\n" |
202 | "Check your installation." , |
203 | progname, full_path); |
204 | else |
205 | pg_log_error("The program \"pg_dump\" was found by \"%s\"\n" |
206 | "but was not the same version as %s.\n" |
207 | "Check your installation." , |
208 | full_path, progname); |
209 | exit_nicely(1); |
210 | } |
211 | |
212 | pgdumpopts = createPQExpBuffer(); |
213 | |
214 | while ((c = getopt_long(argc, argv, "acd:E:f:gh:l:Op:rsS:tU:vwWx" , long_options, &optindex)) != -1) |
215 | { |
216 | switch (c) |
217 | { |
218 | case 'a': |
219 | data_only = true; |
220 | appendPQExpBufferStr(pgdumpopts, " -a" ); |
221 | break; |
222 | |
223 | case 'c': |
224 | output_clean = true; |
225 | break; |
226 | |
227 | case 'd': |
228 | connstr = pg_strdup(optarg); |
229 | break; |
230 | |
231 | case 'E': |
232 | dumpencoding = pg_strdup(optarg); |
233 | appendPQExpBufferStr(pgdumpopts, " -E " ); |
234 | appendShellString(pgdumpopts, optarg); |
235 | break; |
236 | |
237 | case 'f': |
238 | filename = pg_strdup(optarg); |
239 | appendPQExpBufferStr(pgdumpopts, " -f " ); |
240 | appendShellString(pgdumpopts, filename); |
241 | break; |
242 | |
243 | case 'g': |
244 | globals_only = true; |
245 | break; |
246 | |
247 | case 'h': |
248 | pghost = pg_strdup(optarg); |
249 | break; |
250 | |
251 | case 'l': |
252 | pgdb = pg_strdup(optarg); |
253 | break; |
254 | |
255 | case 'O': |
256 | appendPQExpBufferStr(pgdumpopts, " -O" ); |
257 | break; |
258 | |
259 | case 'p': |
260 | pgport = pg_strdup(optarg); |
261 | break; |
262 | |
263 | case 'r': |
264 | roles_only = true; |
265 | break; |
266 | |
267 | case 's': |
268 | appendPQExpBufferStr(pgdumpopts, " -s" ); |
269 | break; |
270 | |
271 | case 'S': |
272 | appendPQExpBufferStr(pgdumpopts, " -S " ); |
273 | appendShellString(pgdumpopts, optarg); |
274 | break; |
275 | |
276 | case 't': |
277 | tablespaces_only = true; |
278 | break; |
279 | |
280 | case 'U': |
281 | pguser = pg_strdup(optarg); |
282 | break; |
283 | |
284 | case 'v': |
285 | verbose = true; |
286 | pg_logging_set_level(PG_LOG_INFO); |
287 | appendPQExpBufferStr(pgdumpopts, " -v" ); |
288 | break; |
289 | |
290 | case 'w': |
291 | prompt_password = TRI_NO; |
292 | appendPQExpBufferStr(pgdumpopts, " -w" ); |
293 | break; |
294 | |
295 | case 'W': |
296 | prompt_password = TRI_YES; |
297 | appendPQExpBufferStr(pgdumpopts, " -W" ); |
298 | break; |
299 | |
300 | case 'x': |
301 | skip_acls = true; |
302 | appendPQExpBufferStr(pgdumpopts, " -x" ); |
303 | break; |
304 | |
305 | case 0: |
306 | break; |
307 | |
308 | case 2: |
309 | appendPQExpBufferStr(pgdumpopts, " --lock-wait-timeout " ); |
310 | appendShellString(pgdumpopts, optarg); |
311 | break; |
312 | |
313 | case 3: |
314 | use_role = pg_strdup(optarg); |
315 | appendPQExpBufferStr(pgdumpopts, " --role " ); |
316 | appendShellString(pgdumpopts, use_role); |
317 | break; |
318 | |
319 | case 4: |
320 | dosync = false; |
321 | appendPQExpBufferStr(pgdumpopts, " --no-sync" ); |
322 | break; |
323 | |
324 | case 5: |
325 | appendPQExpBufferStr(pgdumpopts, " --extra-float-digits " ); |
326 | appendShellString(pgdumpopts, optarg); |
327 | break; |
328 | |
329 | case 6: |
330 | simple_string_list_append(&database_exclude_patterns, optarg); |
331 | break; |
332 | |
333 | case 7: |
334 | appendPQExpBufferStr(pgdumpopts, " --rows-per-insert " ); |
335 | appendShellString(pgdumpopts, optarg); |
336 | break; |
337 | |
338 | default: |
339 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
340 | exit_nicely(1); |
341 | } |
342 | } |
343 | |
344 | /* Complain if any arguments remain */ |
345 | if (optind < argc) |
346 | { |
347 | pg_log_error("too many command-line arguments (first is \"%s\")" , |
348 | argv[optind]); |
349 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
350 | progname); |
351 | exit_nicely(1); |
352 | } |
353 | |
354 | if (database_exclude_patterns.head != NULL && |
355 | (globals_only || roles_only || tablespaces_only)) |
356 | { |
357 | pg_log_error("option --exclude-database cannot be used together with -g/--globals-only, -r/--roles-only, or -t/--tablespaces-only" ); |
358 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
359 | progname); |
360 | exit_nicely(1); |
361 | } |
362 | |
363 | /* Make sure the user hasn't specified a mix of globals-only options */ |
364 | if (globals_only && roles_only) |
365 | { |
366 | pg_log_error("options -g/--globals-only and -r/--roles-only cannot be used together" ); |
367 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
368 | progname); |
369 | exit_nicely(1); |
370 | } |
371 | |
372 | if (globals_only && tablespaces_only) |
373 | { |
374 | pg_log_error("options -g/--globals-only and -t/--tablespaces-only cannot be used together" ); |
375 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
376 | progname); |
377 | exit_nicely(1); |
378 | } |
379 | |
380 | if (if_exists && !output_clean) |
381 | { |
382 | pg_log_error("option --if-exists requires option -c/--clean" ); |
383 | exit_nicely(1); |
384 | } |
385 | |
386 | if (roles_only && tablespaces_only) |
387 | { |
388 | pg_log_error("options -r/--roles-only and -t/--tablespaces-only cannot be used together" ); |
389 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
390 | progname); |
391 | exit_nicely(1); |
392 | } |
393 | |
394 | /* |
395 | * If password values are not required in the dump, switch to using |
396 | * pg_roles which is equally useful, just more likely to have unrestricted |
397 | * access than pg_authid. |
398 | */ |
399 | if (no_role_passwords) |
400 | sprintf(role_catalog, "%s" , PG_ROLES); |
401 | else |
402 | sprintf(role_catalog, "%s" , PG_AUTHID); |
403 | |
404 | /* Add long options to the pg_dump argument list */ |
405 | if (binary_upgrade) |
406 | appendPQExpBufferStr(pgdumpopts, " --binary-upgrade" ); |
407 | if (column_inserts) |
408 | appendPQExpBufferStr(pgdumpopts, " --column-inserts" ); |
409 | if (disable_dollar_quoting) |
410 | appendPQExpBufferStr(pgdumpopts, " --disable-dollar-quoting" ); |
411 | if (disable_triggers) |
412 | appendPQExpBufferStr(pgdumpopts, " --disable-triggers" ); |
413 | if (inserts) |
414 | appendPQExpBufferStr(pgdumpopts, " --inserts" ); |
415 | if (no_tablespaces) |
416 | appendPQExpBufferStr(pgdumpopts, " --no-tablespaces" ); |
417 | if (quote_all_identifiers) |
418 | appendPQExpBufferStr(pgdumpopts, " --quote-all-identifiers" ); |
419 | if (load_via_partition_root) |
420 | appendPQExpBufferStr(pgdumpopts, " --load-via-partition-root" ); |
421 | if (use_setsessauth) |
422 | appendPQExpBufferStr(pgdumpopts, " --use-set-session-authorization" ); |
423 | if (no_comments) |
424 | appendPQExpBufferStr(pgdumpopts, " --no-comments" ); |
425 | if (no_publications) |
426 | appendPQExpBufferStr(pgdumpopts, " --no-publications" ); |
427 | if (no_security_labels) |
428 | appendPQExpBufferStr(pgdumpopts, " --no-security-labels" ); |
429 | if (no_subscriptions) |
430 | appendPQExpBufferStr(pgdumpopts, " --no-subscriptions" ); |
431 | if (no_unlogged_table_data) |
432 | appendPQExpBufferStr(pgdumpopts, " --no-unlogged-table-data" ); |
433 | if (on_conflict_do_nothing) |
434 | appendPQExpBufferStr(pgdumpopts, " --on-conflict-do-nothing" ); |
435 | |
436 | /* |
437 | * If there was a database specified on the command line, use that, |
438 | * otherwise try to connect to database "postgres", and failing that |
439 | * "template1". "postgres" is the preferred choice for 8.1 and later |
440 | * servers, but it usually will not exist on older ones. |
441 | */ |
442 | if (pgdb) |
443 | { |
444 | conn = connectDatabase(pgdb, connstr, pghost, pgport, pguser, |
445 | prompt_password, false); |
446 | |
447 | if (!conn) |
448 | { |
449 | pg_log_error("could not connect to database \"%s\"" , pgdb); |
450 | exit_nicely(1); |
451 | } |
452 | } |
453 | else |
454 | { |
455 | conn = connectDatabase("postgres" , connstr, pghost, pgport, pguser, |
456 | prompt_password, false); |
457 | if (!conn) |
458 | conn = connectDatabase("template1" , connstr, pghost, pgport, pguser, |
459 | prompt_password, true); |
460 | |
461 | if (!conn) |
462 | { |
463 | pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n" |
464 | "Please specify an alternative database." ); |
465 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
466 | progname); |
467 | exit_nicely(1); |
468 | } |
469 | } |
470 | |
471 | /* |
472 | * Get a list of database names that match the exclude patterns |
473 | */ |
474 | expand_dbname_patterns(conn, &database_exclude_patterns, |
475 | &database_exclude_names); |
476 | |
477 | /* |
478 | * Open the output file if required, otherwise use stdout |
479 | */ |
480 | if (filename) |
481 | { |
482 | OPF = fopen(filename, PG_BINARY_W); |
483 | if (!OPF) |
484 | { |
485 | pg_log_error("could not open output file \"%s\": %m" , |
486 | filename); |
487 | exit_nicely(1); |
488 | } |
489 | } |
490 | else |
491 | OPF = stdout; |
492 | |
493 | /* |
494 | * Set the client encoding if requested. |
495 | */ |
496 | if (dumpencoding) |
497 | { |
498 | if (PQsetClientEncoding(conn, dumpencoding) < 0) |
499 | { |
500 | pg_log_error("invalid client encoding \"%s\" specified" , |
501 | dumpencoding); |
502 | exit_nicely(1); |
503 | } |
504 | } |
505 | |
506 | /* |
507 | * Get the active encoding and the standard_conforming_strings setting, so |
508 | * we know how to escape strings. |
509 | */ |
510 | encoding = PQclientEncoding(conn); |
511 | std_strings = PQparameterStatus(conn, "standard_conforming_strings" ); |
512 | if (!std_strings) |
513 | std_strings = "off" ; |
514 | |
515 | /* Set the role if requested */ |
516 | if (use_role && server_version >= 80100) |
517 | { |
518 | PQExpBuffer query = createPQExpBuffer(); |
519 | |
520 | appendPQExpBuffer(query, "SET ROLE %s" , fmtId(use_role)); |
521 | executeCommand(conn, query->data); |
522 | destroyPQExpBuffer(query); |
523 | } |
524 | |
525 | /* Force quoting of all identifiers if requested. */ |
526 | if (quote_all_identifiers && server_version >= 90100) |
527 | executeCommand(conn, "SET quote_all_identifiers = true" ); |
528 | |
529 | fprintf(OPF, "--\n-- PostgreSQL database cluster dump\n--\n\n" ); |
530 | if (verbose) |
531 | dumpTimestamp("Started on" ); |
532 | |
533 | /* |
534 | * We used to emit \connect postgres here, but that served no purpose |
535 | * other than to break things for installations without a postgres |
536 | * database. Everything we're restoring here is a global, so whichever |
537 | * database we're connected to at the moment is fine. |
538 | */ |
539 | |
540 | /* Restore will need to write to the target cluster */ |
541 | fprintf(OPF, "SET default_transaction_read_only = off;\n\n" ); |
542 | |
543 | /* Replicate encoding and std_strings in output */ |
544 | fprintf(OPF, "SET client_encoding = '%s';\n" , |
545 | pg_encoding_to_char(encoding)); |
546 | fprintf(OPF, "SET standard_conforming_strings = %s;\n" , std_strings); |
547 | if (strcmp(std_strings, "off" ) == 0) |
548 | fprintf(OPF, "SET escape_string_warning = off;\n" ); |
549 | fprintf(OPF, "\n" ); |
550 | |
551 | if (!data_only) |
552 | { |
553 | /* |
554 | * If asked to --clean, do that first. We can avoid detailed |
555 | * dependency analysis because databases never depend on each other, |
556 | * and tablespaces never depend on each other. Roles could have |
557 | * grants to each other, but DROP ROLE will clean those up silently. |
558 | */ |
559 | if (output_clean) |
560 | { |
561 | if (!globals_only && !roles_only && !tablespaces_only) |
562 | dropDBs(conn); |
563 | |
564 | if (!roles_only && !no_tablespaces) |
565 | dropTablespaces(conn); |
566 | |
567 | if (!tablespaces_only) |
568 | dropRoles(conn); |
569 | } |
570 | |
571 | /* |
572 | * Now create objects as requested. Be careful that option logic here |
573 | * is the same as for drops above. |
574 | */ |
575 | if (!tablespaces_only) |
576 | { |
577 | /* Dump roles (users) */ |
578 | dumpRoles(conn); |
579 | |
580 | /* Dump role memberships --- need different method for pre-8.1 */ |
581 | if (server_version >= 80100) |
582 | dumpRoleMembership(conn); |
583 | else |
584 | dumpGroups(conn); |
585 | } |
586 | |
587 | /* Dump tablespaces */ |
588 | if (!roles_only && !no_tablespaces) |
589 | dumpTablespaces(conn); |
590 | } |
591 | |
592 | if (!globals_only && !roles_only && !tablespaces_only) |
593 | dumpDatabases(conn); |
594 | |
595 | PQfinish(conn); |
596 | |
597 | if (verbose) |
598 | dumpTimestamp("Completed on" ); |
599 | fprintf(OPF, "--\n-- PostgreSQL database cluster dump complete\n--\n\n" ); |
600 | |
601 | if (filename) |
602 | { |
603 | fclose(OPF); |
604 | |
605 | /* sync the resulting file, errors are not fatal */ |
606 | if (dosync) |
607 | (void) fsync_fname(filename, false); |
608 | } |
609 | |
610 | exit_nicely(0); |
611 | } |
612 | |
613 | |
614 | static void |
615 | help(void) |
616 | { |
617 | printf(_("%s extracts a PostgreSQL database cluster into an SQL script file.\n\n" ), progname); |
618 | printf(_("Usage:\n" )); |
619 | printf(_(" %s [OPTION]...\n" ), progname); |
620 | |
621 | printf(_("\nGeneral options:\n" )); |
622 | printf(_(" -f, --file=FILENAME output file name\n" )); |
623 | printf(_(" -v, --verbose verbose mode\n" )); |
624 | printf(_(" -V, --version output version information, then exit\n" )); |
625 | printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n" )); |
626 | printf(_(" -?, --help show this help, then exit\n" )); |
627 | printf(_("\nOptions controlling the output content:\n" )); |
628 | printf(_(" -a, --data-only dump only the data, not the schema\n" )); |
629 | printf(_(" -c, --clean clean (drop) databases before recreating\n" )); |
630 | printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n" )); |
631 | printf(_(" -g, --globals-only dump only global objects, no databases\n" )); |
632 | printf(_(" -O, --no-owner skip restoration of object ownership\n" )); |
633 | printf(_(" -r, --roles-only dump only roles, no databases or tablespaces\n" )); |
634 | printf(_(" -s, --schema-only dump only the schema, no data\n" )); |
635 | printf(_(" -S, --superuser=NAME superuser user name to use in the dump\n" )); |
636 | printf(_(" -t, --tablespaces-only dump only tablespaces, no databases or roles\n" )); |
637 | printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n" )); |
638 | printf(_(" --binary-upgrade for use by upgrade utilities only\n" )); |
639 | printf(_(" --column-inserts dump data as INSERT commands with column names\n" )); |
640 | printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n" )); |
641 | printf(_(" --disable-triggers disable triggers during data-only restore\n" )); |
642 | printf(_(" --exclude-database=PATTERN exclude databases whose name matches PATTERN\n" )); |
643 | printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n" )); |
644 | printf(_(" --if-exists use IF EXISTS when dropping objects\n" )); |
645 | printf(_(" --inserts dump data as INSERT commands, rather than COPY\n" )); |
646 | printf(_(" --load-via-partition-root load partitions via the root table\n" )); |
647 | printf(_(" --no-comments do not dump comments\n" )); |
648 | printf(_(" --no-publications do not dump publications\n" )); |
649 | printf(_(" --no-role-passwords do not dump passwords for roles\n" )); |
650 | printf(_(" --no-security-labels do not dump security label assignments\n" )); |
651 | printf(_(" --no-subscriptions do not dump subscriptions\n" )); |
652 | printf(_(" --no-sync do not wait for changes to be written safely to disk\n" )); |
653 | printf(_(" --no-tablespaces do not dump tablespace assignments\n" )); |
654 | printf(_(" --no-unlogged-table-data do not dump unlogged table data\n" )); |
655 | printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n" )); |
656 | printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n" )); |
657 | printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n" )); |
658 | printf(_(" --use-set-session-authorization\n" |
659 | " use SET SESSION AUTHORIZATION commands instead of\n" |
660 | " ALTER OWNER commands to set ownership\n" )); |
661 | |
662 | printf(_("\nConnection options:\n" )); |
663 | printf(_(" -d, --dbname=CONNSTR connect using connection string\n" )); |
664 | printf(_(" -h, --host=HOSTNAME database server host or socket directory\n" )); |
665 | printf(_(" -l, --database=DBNAME alternative default database\n" )); |
666 | printf(_(" -p, --port=PORT database server port number\n" )); |
667 | printf(_(" -U, --username=NAME connect as specified database user\n" )); |
668 | printf(_(" -w, --no-password never prompt for password\n" )); |
669 | printf(_(" -W, --password force password prompt (should happen automatically)\n" )); |
670 | printf(_(" --role=ROLENAME do SET ROLE before dump\n" )); |
671 | |
672 | printf(_("\nIf -f/--file is not used, then the SQL script will be written to the standard\n" |
673 | "output.\n\n" )); |
674 | printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n" )); |
675 | } |
676 | |
677 | |
678 | /* |
679 | * Drop roles |
680 | */ |
681 | static void |
682 | dropRoles(PGconn *conn) |
683 | { |
684 | PQExpBuffer buf = createPQExpBuffer(); |
685 | PGresult *res; |
686 | int i_rolname; |
687 | int i; |
688 | |
689 | if (server_version >= 90600) |
690 | printfPQExpBuffer(buf, |
691 | "SELECT rolname " |
692 | "FROM %s " |
693 | "WHERE rolname !~ '^pg_' " |
694 | "ORDER BY 1" , role_catalog); |
695 | else if (server_version >= 80100) |
696 | printfPQExpBuffer(buf, |
697 | "SELECT rolname " |
698 | "FROM %s " |
699 | "ORDER BY 1" , role_catalog); |
700 | else |
701 | printfPQExpBuffer(buf, |
702 | "SELECT usename as rolname " |
703 | "FROM pg_shadow " |
704 | "UNION " |
705 | "SELECT groname as rolname " |
706 | "FROM pg_group " |
707 | "ORDER BY 1" ); |
708 | |
709 | res = executeQuery(conn, buf->data); |
710 | |
711 | i_rolname = PQfnumber(res, "rolname" ); |
712 | |
713 | if (PQntuples(res) > 0) |
714 | fprintf(OPF, "--\n-- Drop roles\n--\n\n" ); |
715 | |
716 | for (i = 0; i < PQntuples(res); i++) |
717 | { |
718 | const char *rolename; |
719 | |
720 | rolename = PQgetvalue(res, i, i_rolname); |
721 | |
722 | fprintf(OPF, "DROP ROLE %s%s;\n" , |
723 | if_exists ? "IF EXISTS " : "" , |
724 | fmtId(rolename)); |
725 | } |
726 | |
727 | PQclear(res); |
728 | destroyPQExpBuffer(buf); |
729 | |
730 | fprintf(OPF, "\n\n" ); |
731 | } |
732 | |
733 | /* |
734 | * Dump roles |
735 | */ |
736 | static void |
737 | dumpRoles(PGconn *conn) |
738 | { |
739 | PQExpBuffer buf = createPQExpBuffer(); |
740 | PGresult *res; |
741 | int i_oid, |
742 | i_rolname, |
743 | i_rolsuper, |
744 | i_rolinherit, |
745 | i_rolcreaterole, |
746 | i_rolcreatedb, |
747 | i_rolcanlogin, |
748 | i_rolconnlimit, |
749 | i_rolpassword, |
750 | i_rolvaliduntil, |
751 | i_rolreplication, |
752 | i_rolbypassrls, |
753 | , |
754 | i_is_current_user; |
755 | int i; |
756 | |
757 | /* note: rolconfig is dumped later */ |
758 | if (server_version >= 90600) |
759 | printfPQExpBuffer(buf, |
760 | "SELECT oid, rolname, rolsuper, rolinherit, " |
761 | "rolcreaterole, rolcreatedb, " |
762 | "rolcanlogin, rolconnlimit, rolpassword, " |
763 | "rolvaliduntil, rolreplication, rolbypassrls, " |
764 | "pg_catalog.shobj_description(oid, '%s') as rolcomment, " |
765 | "rolname = current_user AS is_current_user " |
766 | "FROM %s " |
767 | "WHERE rolname !~ '^pg_' " |
768 | "ORDER BY 2" , role_catalog, role_catalog); |
769 | else if (server_version >= 90500) |
770 | printfPQExpBuffer(buf, |
771 | "SELECT oid, rolname, rolsuper, rolinherit, " |
772 | "rolcreaterole, rolcreatedb, " |
773 | "rolcanlogin, rolconnlimit, rolpassword, " |
774 | "rolvaliduntil, rolreplication, rolbypassrls, " |
775 | "pg_catalog.shobj_description(oid, '%s') as rolcomment, " |
776 | "rolname = current_user AS is_current_user " |
777 | "FROM %s " |
778 | "ORDER BY 2" , role_catalog, role_catalog); |
779 | else if (server_version >= 90100) |
780 | printfPQExpBuffer(buf, |
781 | "SELECT oid, rolname, rolsuper, rolinherit, " |
782 | "rolcreaterole, rolcreatedb, " |
783 | "rolcanlogin, rolconnlimit, rolpassword, " |
784 | "rolvaliduntil, rolreplication, " |
785 | "false as rolbypassrls, " |
786 | "pg_catalog.shobj_description(oid, '%s') as rolcomment, " |
787 | "rolname = current_user AS is_current_user " |
788 | "FROM %s " |
789 | "ORDER BY 2" , role_catalog, role_catalog); |
790 | else if (server_version >= 80200) |
791 | printfPQExpBuffer(buf, |
792 | "SELECT oid, rolname, rolsuper, rolinherit, " |
793 | "rolcreaterole, rolcreatedb, " |
794 | "rolcanlogin, rolconnlimit, rolpassword, " |
795 | "rolvaliduntil, false as rolreplication, " |
796 | "false as rolbypassrls, " |
797 | "pg_catalog.shobj_description(oid, '%s') as rolcomment, " |
798 | "rolname = current_user AS is_current_user " |
799 | "FROM %s " |
800 | "ORDER BY 2" , role_catalog, role_catalog); |
801 | else if (server_version >= 80100) |
802 | printfPQExpBuffer(buf, |
803 | "SELECT oid, rolname, rolsuper, rolinherit, " |
804 | "rolcreaterole, rolcreatedb, " |
805 | "rolcanlogin, rolconnlimit, rolpassword, " |
806 | "rolvaliduntil, false as rolreplication, " |
807 | "false as rolbypassrls, " |
808 | "null as rolcomment, " |
809 | "rolname = current_user AS is_current_user " |
810 | "FROM %s " |
811 | "ORDER BY 2" , role_catalog); |
812 | else |
813 | printfPQExpBuffer(buf, |
814 | "SELECT 0 as oid, usename as rolname, " |
815 | "usesuper as rolsuper, " |
816 | "true as rolinherit, " |
817 | "usesuper as rolcreaterole, " |
818 | "usecreatedb as rolcreatedb, " |
819 | "true as rolcanlogin, " |
820 | "-1 as rolconnlimit, " |
821 | "passwd as rolpassword, " |
822 | "valuntil as rolvaliduntil, " |
823 | "false as rolreplication, " |
824 | "false as rolbypassrls, " |
825 | "null as rolcomment, " |
826 | "usename = current_user AS is_current_user " |
827 | "FROM pg_shadow " |
828 | "UNION ALL " |
829 | "SELECT 0 as oid, groname as rolname, " |
830 | "false as rolsuper, " |
831 | "true as rolinherit, " |
832 | "false as rolcreaterole, " |
833 | "false as rolcreatedb, " |
834 | "false as rolcanlogin, " |
835 | "-1 as rolconnlimit, " |
836 | "null::text as rolpassword, " |
837 | "null::timestamptz as rolvaliduntil, " |
838 | "false as rolreplication, " |
839 | "false as rolbypassrls, " |
840 | "null as rolcomment, " |
841 | "false AS is_current_user " |
842 | "FROM pg_group " |
843 | "WHERE NOT EXISTS (SELECT 1 FROM pg_shadow " |
844 | " WHERE usename = groname) " |
845 | "ORDER BY 2" ); |
846 | |
847 | res = executeQuery(conn, buf->data); |
848 | |
849 | i_oid = PQfnumber(res, "oid" ); |
850 | i_rolname = PQfnumber(res, "rolname" ); |
851 | i_rolsuper = PQfnumber(res, "rolsuper" ); |
852 | i_rolinherit = PQfnumber(res, "rolinherit" ); |
853 | i_rolcreaterole = PQfnumber(res, "rolcreaterole" ); |
854 | i_rolcreatedb = PQfnumber(res, "rolcreatedb" ); |
855 | i_rolcanlogin = PQfnumber(res, "rolcanlogin" ); |
856 | i_rolconnlimit = PQfnumber(res, "rolconnlimit" ); |
857 | i_rolpassword = PQfnumber(res, "rolpassword" ); |
858 | i_rolvaliduntil = PQfnumber(res, "rolvaliduntil" ); |
859 | i_rolreplication = PQfnumber(res, "rolreplication" ); |
860 | i_rolbypassrls = PQfnumber(res, "rolbypassrls" ); |
861 | i_rolcomment = PQfnumber(res, "rolcomment" ); |
862 | i_is_current_user = PQfnumber(res, "is_current_user" ); |
863 | |
864 | if (PQntuples(res) > 0) |
865 | fprintf(OPF, "--\n-- Roles\n--\n\n" ); |
866 | |
867 | for (i = 0; i < PQntuples(res); i++) |
868 | { |
869 | const char *rolename; |
870 | Oid auth_oid; |
871 | |
872 | auth_oid = atooid(PQgetvalue(res, i, i_oid)); |
873 | rolename = PQgetvalue(res, i, i_rolname); |
874 | |
875 | if (strncmp(rolename, "pg_" , 3) == 0) |
876 | { |
877 | pg_log_warning("role name starting with \"pg_\" skipped (%s)" , |
878 | rolename); |
879 | continue; |
880 | } |
881 | |
882 | resetPQExpBuffer(buf); |
883 | |
884 | if (binary_upgrade) |
885 | { |
886 | appendPQExpBufferStr(buf, "\n-- For binary upgrade, must preserve pg_authid.oid\n" ); |
887 | appendPQExpBuffer(buf, |
888 | "SELECT pg_catalog.binary_upgrade_set_next_pg_authid_oid('%u'::pg_catalog.oid);\n\n" , |
889 | auth_oid); |
890 | } |
891 | |
892 | /* |
893 | * We dump CREATE ROLE followed by ALTER ROLE to ensure that the role |
894 | * will acquire the right properties even if it already exists (ie, it |
895 | * won't hurt for the CREATE to fail). This is particularly important |
896 | * for the role we are connected as, since even with --clean we will |
897 | * have failed to drop it. binary_upgrade cannot generate any errors, |
898 | * so we assume the current role is already created. |
899 | */ |
900 | if (!binary_upgrade || |
901 | strcmp(PQgetvalue(res, i, i_is_current_user), "f" ) == 0) |
902 | appendPQExpBuffer(buf, "CREATE ROLE %s;\n" , fmtId(rolename)); |
903 | appendPQExpBuffer(buf, "ALTER ROLE %s WITH" , fmtId(rolename)); |
904 | |
905 | if (strcmp(PQgetvalue(res, i, i_rolsuper), "t" ) == 0) |
906 | appendPQExpBufferStr(buf, " SUPERUSER" ); |
907 | else |
908 | appendPQExpBufferStr(buf, " NOSUPERUSER" ); |
909 | |
910 | if (strcmp(PQgetvalue(res, i, i_rolinherit), "t" ) == 0) |
911 | appendPQExpBufferStr(buf, " INHERIT" ); |
912 | else |
913 | appendPQExpBufferStr(buf, " NOINHERIT" ); |
914 | |
915 | if (strcmp(PQgetvalue(res, i, i_rolcreaterole), "t" ) == 0) |
916 | appendPQExpBufferStr(buf, " CREATEROLE" ); |
917 | else |
918 | appendPQExpBufferStr(buf, " NOCREATEROLE" ); |
919 | |
920 | if (strcmp(PQgetvalue(res, i, i_rolcreatedb), "t" ) == 0) |
921 | appendPQExpBufferStr(buf, " CREATEDB" ); |
922 | else |
923 | appendPQExpBufferStr(buf, " NOCREATEDB" ); |
924 | |
925 | if (strcmp(PQgetvalue(res, i, i_rolcanlogin), "t" ) == 0) |
926 | appendPQExpBufferStr(buf, " LOGIN" ); |
927 | else |
928 | appendPQExpBufferStr(buf, " NOLOGIN" ); |
929 | |
930 | if (strcmp(PQgetvalue(res, i, i_rolreplication), "t" ) == 0) |
931 | appendPQExpBufferStr(buf, " REPLICATION" ); |
932 | else |
933 | appendPQExpBufferStr(buf, " NOREPLICATION" ); |
934 | |
935 | if (strcmp(PQgetvalue(res, i, i_rolbypassrls), "t" ) == 0) |
936 | appendPQExpBufferStr(buf, " BYPASSRLS" ); |
937 | else |
938 | appendPQExpBufferStr(buf, " NOBYPASSRLS" ); |
939 | |
940 | if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1" ) != 0) |
941 | appendPQExpBuffer(buf, " CONNECTION LIMIT %s" , |
942 | PQgetvalue(res, i, i_rolconnlimit)); |
943 | |
944 | |
945 | if (!PQgetisnull(res, i, i_rolpassword) && !no_role_passwords) |
946 | { |
947 | appendPQExpBufferStr(buf, " PASSWORD " ); |
948 | appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolpassword), conn); |
949 | } |
950 | |
951 | if (!PQgetisnull(res, i, i_rolvaliduntil)) |
952 | appendPQExpBuffer(buf, " VALID UNTIL '%s'" , |
953 | PQgetvalue(res, i, i_rolvaliduntil)); |
954 | |
955 | appendPQExpBufferStr(buf, ";\n" ); |
956 | |
957 | if (!no_comments && !PQgetisnull(res, i, i_rolcomment)) |
958 | { |
959 | appendPQExpBuffer(buf, "COMMENT ON ROLE %s IS " , fmtId(rolename)); |
960 | appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolcomment), conn); |
961 | appendPQExpBufferStr(buf, ";\n" ); |
962 | } |
963 | |
964 | if (!no_security_labels && server_version >= 90200) |
965 | buildShSecLabels(conn, "pg_authid" , auth_oid, |
966 | "ROLE" , rolename, |
967 | buf); |
968 | |
969 | fprintf(OPF, "%s" , buf->data); |
970 | } |
971 | |
972 | /* |
973 | * Dump configuration settings for roles after all roles have been dumped. |
974 | * We do it this way because config settings for roles could mention the |
975 | * names of other roles. |
976 | */ |
977 | for (i = 0; i < PQntuples(res); i++) |
978 | dumpUserConfig(conn, PQgetvalue(res, i, i_rolname)); |
979 | |
980 | PQclear(res); |
981 | |
982 | fprintf(OPF, "\n\n" ); |
983 | |
984 | destroyPQExpBuffer(buf); |
985 | } |
986 | |
987 | |
988 | /* |
989 | * Dump role memberships. This code is used for 8.1 and later servers. |
990 | * |
991 | * Note: we expect dumpRoles already created all the roles, but there is |
992 | * no membership yet. |
993 | */ |
994 | static void |
995 | dumpRoleMembership(PGconn *conn) |
996 | { |
997 | PQExpBuffer buf = createPQExpBuffer(); |
998 | PGresult *res; |
999 | int i; |
1000 | |
1001 | printfPQExpBuffer(buf, "SELECT ur.rolname AS roleid, " |
1002 | "um.rolname AS member, " |
1003 | "a.admin_option, " |
1004 | "ug.rolname AS grantor " |
1005 | "FROM pg_auth_members a " |
1006 | "LEFT JOIN %s ur on ur.oid = a.roleid " |
1007 | "LEFT JOIN %s um on um.oid = a.member " |
1008 | "LEFT JOIN %s ug on ug.oid = a.grantor " |
1009 | "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')" |
1010 | "ORDER BY 1,2,3" , role_catalog, role_catalog, role_catalog); |
1011 | res = executeQuery(conn, buf->data); |
1012 | |
1013 | if (PQntuples(res) > 0) |
1014 | fprintf(OPF, "--\n-- Role memberships\n--\n\n" ); |
1015 | |
1016 | for (i = 0; i < PQntuples(res); i++) |
1017 | { |
1018 | char *roleid = PQgetvalue(res, i, 0); |
1019 | char *member = PQgetvalue(res, i, 1); |
1020 | char *option = PQgetvalue(res, i, 2); |
1021 | |
1022 | fprintf(OPF, "GRANT %s" , fmtId(roleid)); |
1023 | fprintf(OPF, " TO %s" , fmtId(member)); |
1024 | if (*option == 't') |
1025 | fprintf(OPF, " WITH ADMIN OPTION" ); |
1026 | |
1027 | /* |
1028 | * We don't track the grantor very carefully in the backend, so cope |
1029 | * with the possibility that it has been dropped. |
1030 | */ |
1031 | if (!PQgetisnull(res, i, 3)) |
1032 | { |
1033 | char *grantor = PQgetvalue(res, i, 3); |
1034 | |
1035 | fprintf(OPF, " GRANTED BY %s" , fmtId(grantor)); |
1036 | } |
1037 | fprintf(OPF, ";\n" ); |
1038 | } |
1039 | |
1040 | PQclear(res); |
1041 | destroyPQExpBuffer(buf); |
1042 | |
1043 | fprintf(OPF, "\n\n" ); |
1044 | } |
1045 | |
1046 | /* |
1047 | * Dump group memberships from a pre-8.1 server. It's annoying that we |
1048 | * can't share any useful amount of code with the post-8.1 case, but |
1049 | * the catalog representations are too different. |
1050 | * |
1051 | * Note: we expect dumpRoles already created all the roles, but there is |
1052 | * no membership yet. |
1053 | */ |
1054 | static void |
1055 | dumpGroups(PGconn *conn) |
1056 | { |
1057 | PQExpBuffer buf = createPQExpBuffer(); |
1058 | PGresult *res; |
1059 | int i; |
1060 | |
1061 | res = executeQuery(conn, |
1062 | "SELECT groname, grolist FROM pg_group ORDER BY 1" ); |
1063 | |
1064 | if (PQntuples(res) > 0) |
1065 | fprintf(OPF, "--\n-- Role memberships\n--\n\n" ); |
1066 | |
1067 | for (i = 0; i < PQntuples(res); i++) |
1068 | { |
1069 | char *groname = PQgetvalue(res, i, 0); |
1070 | char *grolist = PQgetvalue(res, i, 1); |
1071 | PGresult *res2; |
1072 | int j; |
1073 | |
1074 | /* |
1075 | * Array representation is {1,2,3} ... convert to (1,2,3) |
1076 | */ |
1077 | if (strlen(grolist) < 3) |
1078 | continue; |
1079 | |
1080 | grolist = pg_strdup(grolist); |
1081 | grolist[0] = '('; |
1082 | grolist[strlen(grolist) - 1] = ')'; |
1083 | printfPQExpBuffer(buf, |
1084 | "SELECT usename FROM pg_shadow " |
1085 | "WHERE usesysid IN %s ORDER BY 1" , |
1086 | grolist); |
1087 | free(grolist); |
1088 | |
1089 | res2 = executeQuery(conn, buf->data); |
1090 | |
1091 | for (j = 0; j < PQntuples(res2); j++) |
1092 | { |
1093 | char *usename = PQgetvalue(res2, j, 0); |
1094 | |
1095 | /* |
1096 | * Don't try to grant a role to itself; can happen if old |
1097 | * installation has identically named user and group. |
1098 | */ |
1099 | if (strcmp(groname, usename) == 0) |
1100 | continue; |
1101 | |
1102 | fprintf(OPF, "GRANT %s" , fmtId(groname)); |
1103 | fprintf(OPF, " TO %s;\n" , fmtId(usename)); |
1104 | } |
1105 | |
1106 | PQclear(res2); |
1107 | } |
1108 | |
1109 | PQclear(res); |
1110 | destroyPQExpBuffer(buf); |
1111 | |
1112 | fprintf(OPF, "\n\n" ); |
1113 | } |
1114 | |
1115 | |
1116 | /* |
1117 | * Drop tablespaces. |
1118 | */ |
1119 | static void |
1120 | dropTablespaces(PGconn *conn) |
1121 | { |
1122 | PGresult *res; |
1123 | int i; |
1124 | |
1125 | /* |
1126 | * Get all tablespaces except built-in ones (which we assume are named |
1127 | * pg_xxx) |
1128 | */ |
1129 | res = executeQuery(conn, "SELECT spcname " |
1130 | "FROM pg_catalog.pg_tablespace " |
1131 | "WHERE spcname !~ '^pg_' " |
1132 | "ORDER BY 1" ); |
1133 | |
1134 | if (PQntuples(res) > 0) |
1135 | fprintf(OPF, "--\n-- Drop tablespaces\n--\n\n" ); |
1136 | |
1137 | for (i = 0; i < PQntuples(res); i++) |
1138 | { |
1139 | char *spcname = PQgetvalue(res, i, 0); |
1140 | |
1141 | fprintf(OPF, "DROP TABLESPACE %s%s;\n" , |
1142 | if_exists ? "IF EXISTS " : "" , |
1143 | fmtId(spcname)); |
1144 | } |
1145 | |
1146 | PQclear(res); |
1147 | |
1148 | fprintf(OPF, "\n\n" ); |
1149 | } |
1150 | |
1151 | /* |
1152 | * Dump tablespaces. |
1153 | */ |
1154 | static void |
1155 | dumpTablespaces(PGconn *conn) |
1156 | { |
1157 | PGresult *res; |
1158 | int i; |
1159 | |
1160 | /* |
1161 | * Get all tablespaces except built-in ones (which we assume are named |
1162 | * pg_xxx) |
1163 | * |
1164 | * For the tablespace ACLs, as of 9.6, we extract both the positive (as |
1165 | * spcacl) and negative (as rspcacl) ACLs, relative to the default ACL for |
1166 | * tablespaces, which are then passed to buildACLCommands() below. |
1167 | * |
1168 | * See buildACLQueries() and buildACLCommands(). |
1169 | * |
1170 | * The order in which privileges are in the ACL string (the order they |
1171 | * have been GRANT'd in, which the backend maintains) must be preserved to |
1172 | * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on |
1173 | * those are dumped in the correct order. |
1174 | * |
1175 | * Note that we do not support initial privileges (pg_init_privs) on |
1176 | * tablespaces, so this logic cannot make use of buildACLQueries(). |
1177 | */ |
1178 | if (server_version >= 90600) |
1179 | res = executeQuery(conn, "SELECT oid, spcname, " |
1180 | "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " |
1181 | "pg_catalog.pg_tablespace_location(oid), " |
1182 | "(SELECT array_agg(acl ORDER BY row_n) FROM " |
1183 | " (SELECT acl, row_n FROM " |
1184 | " unnest(coalesce(spcacl,acldefault('t',spcowner))) " |
1185 | " WITH ORDINALITY AS perm(acl,row_n) " |
1186 | " WHERE NOT EXISTS ( " |
1187 | " SELECT 1 " |
1188 | " FROM unnest(acldefault('t',spcowner)) " |
1189 | " AS init(init_acl) " |
1190 | " WHERE acl = init_acl)) AS spcacls) " |
1191 | " AS spcacl, " |
1192 | "(SELECT array_agg(acl ORDER BY row_n) FROM " |
1193 | " (SELECT acl, row_n FROM " |
1194 | " unnest(acldefault('t',spcowner)) " |
1195 | " WITH ORDINALITY AS initp(acl,row_n) " |
1196 | " WHERE NOT EXISTS ( " |
1197 | " SELECT 1 " |
1198 | " FROM unnest(coalesce(spcacl,acldefault('t',spcowner))) " |
1199 | " AS permp(orig_acl) " |
1200 | " WHERE acl = orig_acl)) AS rspcacls) " |
1201 | " AS rspcacl, " |
1202 | "array_to_string(spcoptions, ', ')," |
1203 | "pg_catalog.shobj_description(oid, 'pg_tablespace') " |
1204 | "FROM pg_catalog.pg_tablespace " |
1205 | "WHERE spcname !~ '^pg_' " |
1206 | "ORDER BY 1" ); |
1207 | else if (server_version >= 90200) |
1208 | res = executeQuery(conn, "SELECT oid, spcname, " |
1209 | "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " |
1210 | "pg_catalog.pg_tablespace_location(oid), " |
1211 | "spcacl, '' as rspcacl, " |
1212 | "array_to_string(spcoptions, ', ')," |
1213 | "pg_catalog.shobj_description(oid, 'pg_tablespace') " |
1214 | "FROM pg_catalog.pg_tablespace " |
1215 | "WHERE spcname !~ '^pg_' " |
1216 | "ORDER BY 1" ); |
1217 | else if (server_version >= 90000) |
1218 | res = executeQuery(conn, "SELECT oid, spcname, " |
1219 | "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " |
1220 | "spclocation, spcacl, '' as rspcacl, " |
1221 | "array_to_string(spcoptions, ', ')," |
1222 | "pg_catalog.shobj_description(oid, 'pg_tablespace') " |
1223 | "FROM pg_catalog.pg_tablespace " |
1224 | "WHERE spcname !~ '^pg_' " |
1225 | "ORDER BY 1" ); |
1226 | else if (server_version >= 80200) |
1227 | res = executeQuery(conn, "SELECT oid, spcname, " |
1228 | "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " |
1229 | "spclocation, spcacl, '' as rspcacl, null, " |
1230 | "pg_catalog.shobj_description(oid, 'pg_tablespace') " |
1231 | "FROM pg_catalog.pg_tablespace " |
1232 | "WHERE spcname !~ '^pg_' " |
1233 | "ORDER BY 1" ); |
1234 | else |
1235 | res = executeQuery(conn, "SELECT oid, spcname, " |
1236 | "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " |
1237 | "spclocation, spcacl, '' as rspcacl, " |
1238 | "null, null " |
1239 | "FROM pg_catalog.pg_tablespace " |
1240 | "WHERE spcname !~ '^pg_' " |
1241 | "ORDER BY 1" ); |
1242 | |
1243 | if (PQntuples(res) > 0) |
1244 | fprintf(OPF, "--\n-- Tablespaces\n--\n\n" ); |
1245 | |
1246 | for (i = 0; i < PQntuples(res); i++) |
1247 | { |
1248 | PQExpBuffer buf = createPQExpBuffer(); |
1249 | Oid spcoid = atooid(PQgetvalue(res, i, 0)); |
1250 | char *spcname = PQgetvalue(res, i, 1); |
1251 | char *spcowner = PQgetvalue(res, i, 2); |
1252 | char *spclocation = PQgetvalue(res, i, 3); |
1253 | char *spcacl = PQgetvalue(res, i, 4); |
1254 | char *rspcacl = PQgetvalue(res, i, 5); |
1255 | char *spcoptions = PQgetvalue(res, i, 6); |
1256 | char * = PQgetvalue(res, i, 7); |
1257 | char *fspcname; |
1258 | |
1259 | /* needed for buildACLCommands() */ |
1260 | fspcname = pg_strdup(fmtId(spcname)); |
1261 | |
1262 | appendPQExpBuffer(buf, "CREATE TABLESPACE %s" , fspcname); |
1263 | appendPQExpBuffer(buf, " OWNER %s" , fmtId(spcowner)); |
1264 | |
1265 | appendPQExpBufferStr(buf, " LOCATION " ); |
1266 | appendStringLiteralConn(buf, spclocation, conn); |
1267 | appendPQExpBufferStr(buf, ";\n" ); |
1268 | |
1269 | if (spcoptions && spcoptions[0] != '\0') |
1270 | appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n" , |
1271 | fspcname, spcoptions); |
1272 | |
1273 | if (!skip_acls && |
1274 | !buildACLCommands(fspcname, NULL, NULL, "TABLESPACE" , |
1275 | spcacl, rspcacl, |
1276 | spcowner, "" , server_version, buf)) |
1277 | { |
1278 | pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"" , |
1279 | spcacl, spcname); |
1280 | PQfinish(conn); |
1281 | exit_nicely(1); |
1282 | } |
1283 | |
1284 | if (!no_comments && spccomment && spccomment[0] != '\0') |
1285 | { |
1286 | appendPQExpBuffer(buf, "COMMENT ON TABLESPACE %s IS " , fspcname); |
1287 | appendStringLiteralConn(buf, spccomment, conn); |
1288 | appendPQExpBufferStr(buf, ";\n" ); |
1289 | } |
1290 | |
1291 | if (!no_security_labels && server_version >= 90200) |
1292 | buildShSecLabels(conn, "pg_tablespace" , spcoid, |
1293 | "TABLESPACE" , spcname, |
1294 | buf); |
1295 | |
1296 | fprintf(OPF, "%s" , buf->data); |
1297 | |
1298 | free(fspcname); |
1299 | destroyPQExpBuffer(buf); |
1300 | } |
1301 | |
1302 | PQclear(res); |
1303 | fprintf(OPF, "\n\n" ); |
1304 | } |
1305 | |
1306 | |
1307 | /* |
1308 | * Dump commands to drop each database. |
1309 | */ |
1310 | static void |
1311 | dropDBs(PGconn *conn) |
1312 | { |
1313 | PGresult *res; |
1314 | int i; |
1315 | |
1316 | /* |
1317 | * Skip databases marked not datallowconn, since we'd be unable to connect |
1318 | * to them anyway. This must agree with dumpDatabases(). |
1319 | */ |
1320 | res = executeQuery(conn, |
1321 | "SELECT datname " |
1322 | "FROM pg_database d " |
1323 | "WHERE datallowconn " |
1324 | "ORDER BY datname" ); |
1325 | |
1326 | if (PQntuples(res) > 0) |
1327 | fprintf(OPF, "--\n-- Drop databases (except postgres and template1)\n--\n\n" ); |
1328 | |
1329 | for (i = 0; i < PQntuples(res); i++) |
1330 | { |
1331 | char *dbname = PQgetvalue(res, i, 0); |
1332 | |
1333 | /* |
1334 | * Skip "postgres" and "template1"; dumpDatabases() will deal with |
1335 | * them specially. Also, be sure to skip "template0", even if for |
1336 | * some reason it's not marked !datallowconn. |
1337 | */ |
1338 | if (strcmp(dbname, "template1" ) != 0 && |
1339 | strcmp(dbname, "template0" ) != 0 && |
1340 | strcmp(dbname, "postgres" ) != 0) |
1341 | { |
1342 | fprintf(OPF, "DROP DATABASE %s%s;\n" , |
1343 | if_exists ? "IF EXISTS " : "" , |
1344 | fmtId(dbname)); |
1345 | } |
1346 | } |
1347 | |
1348 | PQclear(res); |
1349 | |
1350 | fprintf(OPF, "\n\n" ); |
1351 | } |
1352 | |
1353 | |
1354 | /* |
1355 | * Dump user-specific configuration |
1356 | */ |
1357 | static void |
1358 | dumpUserConfig(PGconn *conn, const char *username) |
1359 | { |
1360 | PQExpBuffer buf = createPQExpBuffer(); |
1361 | int count = 1; |
1362 | bool first = true; |
1363 | |
1364 | for (;;) |
1365 | { |
1366 | PGresult *res; |
1367 | |
1368 | if (server_version >= 90000) |
1369 | printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting WHERE " |
1370 | "setdatabase = 0 AND setrole = " |
1371 | "(SELECT oid FROM %s WHERE rolname = " , count, role_catalog); |
1372 | else if (server_version >= 80100) |
1373 | printfPQExpBuffer(buf, "SELECT rolconfig[%d] FROM %s WHERE rolname = " , count, role_catalog); |
1374 | else |
1375 | printfPQExpBuffer(buf, "SELECT useconfig[%d] FROM pg_shadow WHERE usename = " , count); |
1376 | appendStringLiteralConn(buf, username, conn); |
1377 | if (server_version >= 90000) |
1378 | appendPQExpBufferChar(buf, ')'); |
1379 | |
1380 | res = executeQuery(conn, buf->data); |
1381 | if (PQntuples(res) == 1 && |
1382 | !PQgetisnull(res, 0, 0)) |
1383 | { |
1384 | /* comment at section start, only if needed */ |
1385 | if (first) |
1386 | { |
1387 | fprintf(OPF, "--\n-- User Configurations\n--\n\n" ); |
1388 | first = false; |
1389 | } |
1390 | |
1391 | fprintf(OPF, "--\n-- User Config \"%s\"\n--\n\n" , username); |
1392 | resetPQExpBuffer(buf); |
1393 | makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0), |
1394 | "ROLE" , username, NULL, NULL, |
1395 | buf); |
1396 | fprintf(OPF, "%s" , buf->data); |
1397 | PQclear(res); |
1398 | count++; |
1399 | } |
1400 | else |
1401 | { |
1402 | PQclear(res); |
1403 | break; |
1404 | } |
1405 | } |
1406 | |
1407 | destroyPQExpBuffer(buf); |
1408 | } |
1409 | |
1410 | /* |
1411 | * Find a list of database names that match the given patterns. |
1412 | * See also expand_table_name_patterns() in pg_dump.c |
1413 | */ |
1414 | static void |
1415 | expand_dbname_patterns(PGconn *conn, |
1416 | SimpleStringList *patterns, |
1417 | SimpleStringList *names) |
1418 | { |
1419 | PQExpBuffer query; |
1420 | PGresult *res; |
1421 | |
1422 | if (patterns->head == NULL) |
1423 | return; /* nothing to do */ |
1424 | |
1425 | query = createPQExpBuffer(); |
1426 | |
1427 | /* |
1428 | * The loop below runs multiple SELECTs, which might sometimes result in |
1429 | * duplicate entries in the name list, but we don't care, since all we're |
1430 | * going to do is test membership of the list. |
1431 | */ |
1432 | |
1433 | for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next) |
1434 | { |
1435 | appendPQExpBuffer(query, |
1436 | "SELECT datname FROM pg_catalog.pg_database n\n" ); |
1437 | processSQLNamePattern(conn, query, cell->val, false, |
1438 | false, NULL, "datname" , NULL, NULL); |
1439 | |
1440 | res = executeQuery(conn, query->data); |
1441 | for (int i = 0; i < PQntuples(res); i++) |
1442 | { |
1443 | simple_string_list_append(names, PQgetvalue(res, i, 0)); |
1444 | } |
1445 | |
1446 | PQclear(res); |
1447 | resetPQExpBuffer(query); |
1448 | } |
1449 | |
1450 | destroyPQExpBuffer(query); |
1451 | } |
1452 | |
1453 | /* |
1454 | * Dump contents of databases. |
1455 | */ |
1456 | static void |
1457 | dumpDatabases(PGconn *conn) |
1458 | { |
1459 | PGresult *res; |
1460 | int i; |
1461 | |
1462 | /* |
1463 | * Skip databases marked not datallowconn, since we'd be unable to connect |
1464 | * to them anyway. This must agree with dropDBs(). |
1465 | * |
1466 | * We arrange for template1 to be processed first, then we process other |
1467 | * DBs in alphabetical order. If we just did them all alphabetically, we |
1468 | * might find ourselves trying to drop the "postgres" database while still |
1469 | * connected to it. This makes trying to run the restore script while |
1470 | * connected to "template1" a bad idea, but there's no fixed order that |
1471 | * doesn't have some failure mode with --clean. |
1472 | */ |
1473 | res = executeQuery(conn, |
1474 | "SELECT datname " |
1475 | "FROM pg_database d " |
1476 | "WHERE datallowconn " |
1477 | "ORDER BY (datname <> 'template1'), datname" ); |
1478 | |
1479 | if (PQntuples(res) > 0) |
1480 | fprintf(OPF, "--\n-- Databases\n--\n\n" ); |
1481 | |
1482 | for (i = 0; i < PQntuples(res); i++) |
1483 | { |
1484 | char *dbname = PQgetvalue(res, i, 0); |
1485 | const char *create_opts; |
1486 | int ret; |
1487 | |
1488 | /* Skip template0, even if it's not marked !datallowconn. */ |
1489 | if (strcmp(dbname, "template0" ) == 0) |
1490 | continue; |
1491 | |
1492 | /* Skip any explicitly excluded database */ |
1493 | if (simple_string_list_member(&database_exclude_names, dbname)) |
1494 | { |
1495 | pg_log_info("excluding database \"%s\"" , dbname); |
1496 | continue; |
1497 | } |
1498 | |
1499 | pg_log_info("dumping database \"%s\"" , dbname); |
1500 | |
1501 | fprintf(OPF, "--\n-- Database \"%s\" dump\n--\n\n" , dbname); |
1502 | |
1503 | /* |
1504 | * We assume that "template1" and "postgres" already exist in the |
1505 | * target installation. dropDBs() won't have removed them, for fear |
1506 | * of removing the DB the restore script is initially connected to. If |
1507 | * --clean was specified, tell pg_dump to drop and recreate them; |
1508 | * otherwise we'll merely restore their contents. Other databases |
1509 | * should simply be created. |
1510 | */ |
1511 | if (strcmp(dbname, "template1" ) == 0 || strcmp(dbname, "postgres" ) == 0) |
1512 | { |
1513 | if (output_clean) |
1514 | create_opts = "--clean --create" ; |
1515 | else |
1516 | { |
1517 | create_opts = "" ; |
1518 | /* Since pg_dump won't emit a \connect command, we must */ |
1519 | fprintf(OPF, "\\connect %s\n\n" , dbname); |
1520 | } |
1521 | } |
1522 | else |
1523 | create_opts = "--create" ; |
1524 | |
1525 | if (filename) |
1526 | fclose(OPF); |
1527 | |
1528 | ret = runPgDump(dbname, create_opts); |
1529 | if (ret != 0) |
1530 | { |
1531 | pg_log_error("pg_dump failed on database \"%s\", exiting" , dbname); |
1532 | exit_nicely(1); |
1533 | } |
1534 | |
1535 | if (filename) |
1536 | { |
1537 | OPF = fopen(filename, PG_BINARY_A); |
1538 | if (!OPF) |
1539 | { |
1540 | pg_log_error("could not re-open the output file \"%s\": %m" , |
1541 | filename); |
1542 | exit_nicely(1); |
1543 | } |
1544 | } |
1545 | |
1546 | } |
1547 | |
1548 | PQclear(res); |
1549 | } |
1550 | |
1551 | |
1552 | |
1553 | /* |
1554 | * Run pg_dump on dbname, with specified options. |
1555 | */ |
1556 | static int |
1557 | runPgDump(const char *dbname, const char *create_opts) |
1558 | { |
1559 | PQExpBuffer connstrbuf = createPQExpBuffer(); |
1560 | PQExpBuffer cmd = createPQExpBuffer(); |
1561 | int ret; |
1562 | |
1563 | appendPQExpBuffer(cmd, "\"%s\" %s %s" , pg_dump_bin, |
1564 | pgdumpopts->data, create_opts); |
1565 | |
1566 | /* |
1567 | * If we have a filename, use the undocumented plain-append pg_dump |
1568 | * format. |
1569 | */ |
1570 | if (filename) |
1571 | appendPQExpBufferStr(cmd, " -Fa " ); |
1572 | else |
1573 | appendPQExpBufferStr(cmd, " -Fp " ); |
1574 | |
1575 | /* |
1576 | * Append the database name to the already-constructed stem of connection |
1577 | * string. |
1578 | */ |
1579 | appendPQExpBuffer(connstrbuf, "%s dbname=" , connstr); |
1580 | appendConnStrVal(connstrbuf, dbname); |
1581 | |
1582 | appendShellString(cmd, connstrbuf->data); |
1583 | |
1584 | pg_log_info("running \"%s\"" , cmd->data); |
1585 | |
1586 | fflush(stdout); |
1587 | fflush(stderr); |
1588 | |
1589 | ret = system(cmd->data); |
1590 | |
1591 | destroyPQExpBuffer(cmd); |
1592 | destroyPQExpBuffer(connstrbuf); |
1593 | |
1594 | return ret; |
1595 | } |
1596 | |
1597 | /* |
1598 | * buildShSecLabels |
1599 | * |
1600 | * Build SECURITY LABEL command(s) for a shared object |
1601 | * |
1602 | * The caller has to provide object type and identity in two separate formats: |
1603 | * catalog_name (e.g., "pg_database") and object OID, as well as |
1604 | * type name (e.g., "DATABASE") and object name (not pre-quoted). |
1605 | * |
1606 | * The command(s) are appended to "buffer". |
1607 | */ |
1608 | static void |
1609 | buildShSecLabels(PGconn *conn, const char *catalog_name, Oid objectId, |
1610 | const char *objtype, const char *objname, |
1611 | PQExpBuffer buffer) |
1612 | { |
1613 | PQExpBuffer sql = createPQExpBuffer(); |
1614 | PGresult *res; |
1615 | |
1616 | buildShSecLabelQuery(conn, catalog_name, objectId, sql); |
1617 | res = executeQuery(conn, sql->data); |
1618 | emitShSecLabels(conn, res, buffer, objtype, objname); |
1619 | |
1620 | PQclear(res); |
1621 | destroyPQExpBuffer(sql); |
1622 | } |
1623 | |
1624 | /* |
1625 | * Make a database connection with the given parameters. An |
1626 | * interactive password prompt is automatically issued if required. |
1627 | * |
1628 | * If fail_on_error is false, we return NULL without printing any message |
1629 | * on failure, but preserve any prompted password for the next try. |
1630 | * |
1631 | * On success, the global variable 'connstr' is set to a connection string |
1632 | * containing the options used. |
1633 | */ |
1634 | static PGconn * |
1635 | connectDatabase(const char *dbname, const char *connection_string, |
1636 | const char *pghost, const char *pgport, const char *pguser, |
1637 | trivalue prompt_password, bool fail_on_error) |
1638 | { |
1639 | PGconn *conn; |
1640 | bool new_pass; |
1641 | const char *remoteversion_str; |
1642 | int my_version; |
1643 | const char **keywords = NULL; |
1644 | const char **values = NULL; |
1645 | PQconninfoOption *conn_opts = NULL; |
1646 | static bool have_password = false; |
1647 | static char password[100]; |
1648 | |
1649 | if (prompt_password == TRI_YES && !have_password) |
1650 | { |
1651 | simple_prompt("Password: " , password, sizeof(password), false); |
1652 | have_password = true; |
1653 | } |
1654 | |
1655 | /* |
1656 | * Start the connection. Loop until we have a password if requested by |
1657 | * backend. |
1658 | */ |
1659 | do |
1660 | { |
1661 | int argcount = 6; |
1662 | PQconninfoOption *conn_opt; |
1663 | char *err_msg = NULL; |
1664 | int i = 0; |
1665 | |
1666 | if (keywords) |
1667 | free(keywords); |
1668 | if (values) |
1669 | free(values); |
1670 | if (conn_opts) |
1671 | PQconninfoFree(conn_opts); |
1672 | |
1673 | /* |
1674 | * Merge the connection info inputs given in form of connection string |
1675 | * and other options. Explicitly discard any dbname value in the |
1676 | * connection string; otherwise, PQconnectdbParams() would interpret |
1677 | * that value as being itself a connection string. |
1678 | */ |
1679 | if (connection_string) |
1680 | { |
1681 | conn_opts = PQconninfoParse(connection_string, &err_msg); |
1682 | if (conn_opts == NULL) |
1683 | { |
1684 | pg_log_error("%s" , err_msg); |
1685 | exit_nicely(1); |
1686 | } |
1687 | |
1688 | for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) |
1689 | { |
1690 | if (conn_opt->val != NULL && conn_opt->val[0] != '\0' && |
1691 | strcmp(conn_opt->keyword, "dbname" ) != 0) |
1692 | argcount++; |
1693 | } |
1694 | |
1695 | keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); |
1696 | values = pg_malloc0((argcount + 1) * sizeof(*values)); |
1697 | |
1698 | for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) |
1699 | { |
1700 | if (conn_opt->val != NULL && conn_opt->val[0] != '\0' && |
1701 | strcmp(conn_opt->keyword, "dbname" ) != 0) |
1702 | { |
1703 | keywords[i] = conn_opt->keyword; |
1704 | values[i] = conn_opt->val; |
1705 | i++; |
1706 | } |
1707 | } |
1708 | } |
1709 | else |
1710 | { |
1711 | keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); |
1712 | values = pg_malloc0((argcount + 1) * sizeof(*values)); |
1713 | } |
1714 | |
1715 | if (pghost) |
1716 | { |
1717 | keywords[i] = "host" ; |
1718 | values[i] = pghost; |
1719 | i++; |
1720 | } |
1721 | if (pgport) |
1722 | { |
1723 | keywords[i] = "port" ; |
1724 | values[i] = pgport; |
1725 | i++; |
1726 | } |
1727 | if (pguser) |
1728 | { |
1729 | keywords[i] = "user" ; |
1730 | values[i] = pguser; |
1731 | i++; |
1732 | } |
1733 | if (have_password) |
1734 | { |
1735 | keywords[i] = "password" ; |
1736 | values[i] = password; |
1737 | i++; |
1738 | } |
1739 | if (dbname) |
1740 | { |
1741 | keywords[i] = "dbname" ; |
1742 | values[i] = dbname; |
1743 | i++; |
1744 | } |
1745 | keywords[i] = "fallback_application_name" ; |
1746 | values[i] = progname; |
1747 | i++; |
1748 | |
1749 | new_pass = false; |
1750 | conn = PQconnectdbParams(keywords, values, true); |
1751 | |
1752 | if (!conn) |
1753 | { |
1754 | pg_log_error("could not connect to database \"%s\"" , dbname); |
1755 | exit_nicely(1); |
1756 | } |
1757 | |
1758 | if (PQstatus(conn) == CONNECTION_BAD && |
1759 | PQconnectionNeedsPassword(conn) && |
1760 | !have_password && |
1761 | prompt_password != TRI_NO) |
1762 | { |
1763 | PQfinish(conn); |
1764 | simple_prompt("Password: " , password, sizeof(password), false); |
1765 | have_password = true; |
1766 | new_pass = true; |
1767 | } |
1768 | } while (new_pass); |
1769 | |
1770 | /* check to see that the backend connection was successfully made */ |
1771 | if (PQstatus(conn) == CONNECTION_BAD) |
1772 | { |
1773 | if (fail_on_error) |
1774 | { |
1775 | pg_log_error("could not connect to database \"%s\": %s" , |
1776 | dbname, PQerrorMessage(conn)); |
1777 | exit_nicely(1); |
1778 | } |
1779 | else |
1780 | { |
1781 | PQfinish(conn); |
1782 | |
1783 | free(keywords); |
1784 | free(values); |
1785 | PQconninfoFree(conn_opts); |
1786 | |
1787 | return NULL; |
1788 | } |
1789 | } |
1790 | |
1791 | /* |
1792 | * Ok, connected successfully. Remember the options used, in the form of a |
1793 | * connection string. |
1794 | */ |
1795 | connstr = constructConnStr(keywords, values); |
1796 | |
1797 | free(keywords); |
1798 | free(values); |
1799 | PQconninfoFree(conn_opts); |
1800 | |
1801 | /* Check version */ |
1802 | remoteversion_str = PQparameterStatus(conn, "server_version" ); |
1803 | if (!remoteversion_str) |
1804 | { |
1805 | pg_log_error("could not get server version" ); |
1806 | exit_nicely(1); |
1807 | } |
1808 | server_version = PQserverVersion(conn); |
1809 | if (server_version == 0) |
1810 | { |
1811 | pg_log_error("could not parse server version \"%s\"" , |
1812 | remoteversion_str); |
1813 | exit_nicely(1); |
1814 | } |
1815 | |
1816 | my_version = PG_VERSION_NUM; |
1817 | |
1818 | /* |
1819 | * We allow the server to be back to 8.0, and up to any minor release of |
1820 | * our own major version. (See also version check in pg_dump.c.) |
1821 | */ |
1822 | if (my_version != server_version |
1823 | && (server_version < 80000 || |
1824 | (server_version / 100) > (my_version / 100))) |
1825 | { |
1826 | pg_log_error("server version: %s; %s version: %s" , |
1827 | remoteversion_str, progname, PG_VERSION); |
1828 | pg_log_error("aborting because of server version mismatch" ); |
1829 | exit_nicely(1); |
1830 | } |
1831 | |
1832 | PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL)); |
1833 | |
1834 | return conn; |
1835 | } |
1836 | |
1837 | /* ---------- |
1838 | * Construct a connection string from the given keyword/value pairs. It is |
1839 | * used to pass the connection options to the pg_dump subprocess. |
1840 | * |
1841 | * The following parameters are excluded: |
1842 | * dbname - varies in each pg_dump invocation |
1843 | * password - it's not secure to pass a password on the command line |
1844 | * fallback_application_name - we'll let pg_dump set it |
1845 | * ---------- |
1846 | */ |
1847 | static char * |
1848 | constructConnStr(const char **keywords, const char **values) |
1849 | { |
1850 | PQExpBuffer buf = createPQExpBuffer(); |
1851 | char *connstr; |
1852 | int i; |
1853 | bool firstkeyword = true; |
1854 | |
1855 | /* Construct a new connection string in key='value' format. */ |
1856 | for (i = 0; keywords[i] != NULL; i++) |
1857 | { |
1858 | if (strcmp(keywords[i], "dbname" ) == 0 || |
1859 | strcmp(keywords[i], "password" ) == 0 || |
1860 | strcmp(keywords[i], "fallback_application_name" ) == 0) |
1861 | continue; |
1862 | |
1863 | if (!firstkeyword) |
1864 | appendPQExpBufferChar(buf, ' '); |
1865 | firstkeyword = false; |
1866 | appendPQExpBuffer(buf, "%s=" , keywords[i]); |
1867 | appendConnStrVal(buf, values[i]); |
1868 | } |
1869 | |
1870 | connstr = pg_strdup(buf->data); |
1871 | destroyPQExpBuffer(buf); |
1872 | return connstr; |
1873 | } |
1874 | |
1875 | /* |
1876 | * Run a query, return the results, exit program on failure. |
1877 | */ |
1878 | static PGresult * |
1879 | executeQuery(PGconn *conn, const char *query) |
1880 | { |
1881 | PGresult *res; |
1882 | |
1883 | pg_log_info("executing %s" , query); |
1884 | |
1885 | res = PQexec(conn, query); |
1886 | if (!res || |
1887 | PQresultStatus(res) != PGRES_TUPLES_OK) |
1888 | { |
1889 | pg_log_error("query failed: %s" , PQerrorMessage(conn)); |
1890 | pg_log_error("query was: %s" , query); |
1891 | PQfinish(conn); |
1892 | exit_nicely(1); |
1893 | } |
1894 | |
1895 | return res; |
1896 | } |
1897 | |
1898 | /* |
1899 | * As above for a SQL command (which returns nothing). |
1900 | */ |
1901 | static void |
1902 | executeCommand(PGconn *conn, const char *query) |
1903 | { |
1904 | PGresult *res; |
1905 | |
1906 | pg_log_info("executing %s" , query); |
1907 | |
1908 | res = PQexec(conn, query); |
1909 | if (!res || |
1910 | PQresultStatus(res) != PGRES_COMMAND_OK) |
1911 | { |
1912 | pg_log_error("query failed: %s" , PQerrorMessage(conn)); |
1913 | pg_log_error("query was: %s" , query); |
1914 | PQfinish(conn); |
1915 | exit_nicely(1); |
1916 | } |
1917 | |
1918 | PQclear(res); |
1919 | } |
1920 | |
1921 | |
1922 | /* |
1923 | * dumpTimestamp |
1924 | */ |
1925 | static void |
1926 | dumpTimestamp(const char *msg) |
1927 | { |
1928 | char buf[64]; |
1929 | time_t now = time(NULL); |
1930 | |
1931 | if (strftime(buf, sizeof(buf), PGDUMP_STRFTIME_FMT, localtime(&now)) != 0) |
1932 | fprintf(OPF, "-- %s %s\n\n" , msg, buf); |
1933 | } |
1934 | |