pg_basebackup: Avoid unclean failure with server-compression and -D -.
authorRobert Haas <rhaas@postgresql.org>
Fri, 11 Mar 2022 17:22:02 +0000 (12:22 -0500)
committerRobert Haas <rhaas@postgresql.org>
Fri, 11 Mar 2022 17:22:02 +0000 (12:22 -0500)
Fail with a suitable error message instead. We can't inject the backup
manifest into the output tarfile without decompressing it, and if
we did that, we'd have to recompress the tarfile afterwards to produce
the result the user is expecting. While we have enough infrastructure
in pg_basebackup now to accomplish that whole series of steps without
much additional code, it seems like excessively surprising behavior.
The user probably did not select server-side compression with the idea
that the client was going to end up decompressing it and then
recompressing.

Report from Justin Pryzby. Fix by me.

Discussion: http://postgr.es/m/CA+Tgmob6Rnjz-Qv32h3yJn8nnUkLhrtQDAS4y5AtsgtorAFHRA@mail.gmail.com

src/bin/pg_basebackup/pg_basebackup.c

index 9f3ecc60fbe104b8630801ebd0c0ac2c2aad50b9..43c4036eeeda97afe2f51058e92f15ac0f7c4a39 100644 (file)
@@ -1208,7 +1208,8 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
    bool        is_tar,
                is_tar_gz,
                is_tar_lz4,
-               is_tar_zstd;
+               is_tar_zstd,
+               is_compressed_tar;
    bool        must_parse_archive;
    int         archive_name_len = strlen(archive_name);
 
@@ -1235,6 +1236,24 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
    is_tar_zstd = (archive_name_len > 8 &&
                   strcmp(archive_name + archive_name_len - 4, ".zst") == 0);
 
+   /* Is this any kind of compressed tar? */
+   is_compressed_tar = is_tar_gz || is_tar_lz4 || is_tar_zstd;
+
+   /*
+    * Injecting the manifest into a compressed tar file would be possible if
+    * we decompressed it, parsed the tarfile, generated a new tarfile, and
+    * recompressed it, but compressing and decompressing multiple times just
+    * to inject the manifest seems inefficient enough that it's probably not
+    * what the user wants. So, instead, reject the request and tell the user
+    * to specify something more reasonable.
+    */
+   if (inject_manifest && is_compressed_tar)
+   {
+       pg_log_error("cannot inject manifest into a compressed tarfile");
+       pg_log_info("use client-side compression, send the output to a directory rather than standard output, or use --no-manifest");
+       exit(1);
+   }
+
    /*
     * We have to parse the archive if (1) we're suppose to extract it, or if
     * (2) we need to inject backup_manifest or recovery configuration into it.
@@ -1244,8 +1263,7 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
        (spclocation == NULL && writerecoveryconf));
 
    /* At present, we only know how to parse tar archives. */
-   if (must_parse_archive && !is_tar && !is_tar_gz && !is_tar_lz4
-       && !is_tar_zstd)
+   if (must_parse_archive && !is_tar && !is_compressed_tar)
    {
        pg_log_error("unable to parse archive: %s", archive_name);
        pg_log_info("only tar archives can be parsed");