zstd compression for pg_dump
I found that our largest tables are 40% smaller and 20% faster to pipe
pg_dump -Fc -Z0 |zstd relative to native zlib
So I wondered how much better when integrated in pg_dump, and found that
there's some additional improvement, but a big disadvantage of piping through
zstd is that it's not identified as a PGDMP file, and, /usr/bin/file on centos7
fails to even identify zstd by its magic number..
I looked for previous discussion about alternate compressions, but didn't find
anything for pg_dump.
--
Justin
Attachments:
0001-fix-pre-existing-docs-comments.patchtext/x-diff; charset=us-asciiDownload
From c506c8a93ae70726a5b4b33a1d0098caf5665f3a Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 21 Dec 2020 00:32:32 -0600
Subject: [PATCH 1/7] fix pre-existing docs/comments
---
doc/src/sgml/ref/pg_dump.sgml | 2 +-
src/bin/pg_dump/pg_backup_directory.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 0aa35cf0c3..dcb25dc3cd 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -621,7 +621,7 @@ PostgreSQL documentation
<listitem>
<para>
Specify the compression level to use. Zero means no compression.
- For the custom archive format, this specifies compression of
+ For the custom and directory archive formats, this specifies compression of
individual table-data segments, and the default is to compress
at a moderate level.
For plain text output, setting a nonzero compression level causes
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 48fa7cb1a3..650b542fce 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -4,7 +4,7 @@
*
* A directory format dump is a directory, which contains a "toc.dat" file
* for the TOC, and a separate file for each data entry, named "<oid>.dat".
- * Large objects (BLOBs) are stored in separate files named "blob_<uid>.dat",
+ * Large objects (BLOBs) are stored in separate files named "blob_<oid>.dat",
* and there's a plain-text TOC file for them called "blobs.toc". If
* compression is used, each data file is individually compressed and the
* ".gz" suffix is added to the filenames. The TOC files are never
--
2.17.0
0002-Fix-malformed-comment.patchtext/x-diff; charset=us-asciiDownload
From 49f400b5b2e559cc2ed6be82e04a4dc4ba5c63b3 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat, 12 Dec 2020 00:11:37 -0600
Subject: [PATCH 2/7] Fix malformed comment..
broken since bf50caf10.
---
src/bin/pg_dump/pg_backup_archiver.h | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 177360ed6e..6a5a22637b 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -329,9 +329,12 @@ struct _archiveHandle
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
- int compression; /* Compression requested on open Possible
- * values for compression: -1
- * Z_DEFAULT_COMPRESSION 0 COMPRESSION_NONE
+ int compression; /*---------
+ * Compression requested on open
+ * Possible values for compression:
+ * -2 ZSTD_COMPRESSION
+ * -1 Z_DEFAULT_COMPRESSION
+ * 0 COMPRESSION_NONE
* 1-9 levels for gzip compression */
bool dosync; /* data requested to be synced on sight */
ArchiveMode mode; /* File mode - r or w */
--
2.17.0
0003-pg_dump-zstd-compression.patchtext/x-diff; charset=us-asciiDownload
From a645d33c6bed149abae5a47adb40d24812e8b12c Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 11 Dec 2020 15:01:25 -0600
Subject: [PATCH 3/7] pg_dump: zstd compression
---
configure | 123 ++++++-
configure.ac | 22 ++
src/bin/pg_dump/compress_io.c | 475 ++++++++++++++++++++++++--
src/bin/pg_dump/compress_io.h | 5 +-
src/bin/pg_dump/pg_backup_archiver.h | 5 +
src/bin/pg_dump/pg_backup_directory.c | 6 +-
src/bin/pg_dump/pg_dump.c | 3 +-
src/include/pg_config.h.in | 3 +
8 files changed, 597 insertions(+), 45 deletions(-)
diff --git a/configure b/configure
index 11a4284e5b..240e536e04 100755
--- a/configure
+++ b/configure
@@ -698,6 +698,7 @@ with_gnu_ld
LD
LDFLAGS_SL
LDFLAGS_EX
+with_zstd
with_zlib
with_system_tzdata
with_libxslt
@@ -798,6 +799,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -866,6 +868,7 @@ with_libxml
with_libxslt
with_system_tzdata
with_zlib
+with_zstd
with_gnu_ld
enable_largefile
'
@@ -935,6 +938,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1187,6 +1191,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1324,7 +1337,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1477,6 +1490,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1570,6 +1584,7 @@ Optional Packages:
--with-system-tzdata=DIR
use system time zone data in DIR
--without-zlib do not use Zlib
+ --without-zstd do not use Zstd
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
Some influential environment variables:
@@ -8601,6 +8616,35 @@ fi
+#
+# Zstd
+#
+
+
+
+# Check whether --with-zstd was given.
+if test "${with_zstd+set}" = set; then :
+ withval=$with_zstd;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ as_fn_error $? "no argument expected for --with-zstd option" "$LINENO" 5
+ ;;
+ esac
+
+else
+ with_zstd=yes
+
+fi
+
+
+
+
#
# Assignments
#
@@ -12092,6 +12136,59 @@ fi
fi
+if test "$with_zstd" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZSTD_compressStream2 in -lzstd" >&5
+$as_echo_n "checking for ZSTD_compressStream2 in -lzstd... " >&6; }
+if ${ac_cv_lib_zstd_ZSTD_compressStream2+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzstd $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ZSTD_compressStream2 ();
+int
+main ()
+{
+return ZSTD_compressStream2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_zstd_ZSTD_compressStream2=yes
+else
+ ac_cv_lib_zstd_ZSTD_compressStream2=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_zstd_ZSTD_compressStream2" >&5
+$as_echo "$ac_cv_lib_zstd_ZSTD_compressStream2" >&6; }
+if test "x$ac_cv_lib_zstd_ZSTD_compressStream2" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZSTD 1
+_ACEOF
+
+ LIBS="-lzstd $LIBS"
+
+else
+ as_fn_error $? "zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+fi
+
if test "$enable_spinlocks" = yes; then
$as_echo "#define HAVE_SPINLOCKS 1" >>confdefs.h
@@ -13295,6 +13392,20 @@ Use --without-zlib to disable zlib support." "$LINENO" 5
fi
+fi
+
+if test "$with_zstd" = yes; then
+ ac_fn_c_check_header_mongrel "$LINENO" "zstd.h" "ac_cv_header_zstd_h" "$ac_includes_default"
+if test "x$ac_cv_header_zstd_h" = xyes; then :
+
+else
+ as_fn_error $? "zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+
fi
if test "$with_gssapi" = yes ; then
@@ -14689,7 +14800,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14735,7 +14846,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14759,7 +14870,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14804,7 +14915,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14828,7 +14939,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
diff --git a/configure.ac b/configure.ac
index fc523c6aeb..7f7222159a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -999,6 +999,13 @@ PGAC_ARG_BOOL(with, zlib, yes,
[do not use Zlib])
AC_SUBST(with_zlib)
+#
+# Zstd
+#
+PGAC_ARG_BOOL(with, zstd, yes,
+ [do not use Zstd])
+AC_SUBST(with_zstd)
+
#
# Assignments
#
@@ -1186,6 +1193,14 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_LIB(zstd, ZSTD_compressStream2, [],
+ [AC_MSG_ERROR([zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$enable_spinlocks" = yes; then
AC_DEFINE(HAVE_SPINLOCKS, 1, [Define to 1 if you have spinlocks.])
else
@@ -1400,6 +1415,13 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_HEADER(zstd.h, [], [AC_MSG_ERROR([zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$with_gssapi" = yes ; then
AC_CHECK_HEADERS(gssapi/gssapi.h, [],
[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 1417401086..b51ba680a2 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -13,7 +13,7 @@
* friends, providing an interface similar to those, but abstracts away
* the possible compression. Both APIs use libz for the compression, but
* the second API uses gzip headers, so the resulting files can be easily
- * manipulated with the gzip utility.
+ * manipulated with the gzip utility. XXX
*
* Compressor API
* --------------
@@ -41,7 +41,7 @@
* libz's gzopen() APIs. It allows you to use the same functions for
* compressed and uncompressed streams. cfopen_read() first tries to open
* the file with given name, and if it fails, it tries to open the same
- * file with the .gz suffix. cfopen_write() opens a file for writing, an
+ * file with the .gz suffix. cfopen_write() opens a file for writing, an XXX
* extra argument specifies if the file should be compressed, and adds the
* .gz suffix to the filename if so. This allows you to easily handle both
* compressed and uncompressed files.
@@ -72,6 +72,18 @@ struct CompressorState
char *zlibOut;
size_t zlibOutSize;
#endif
+
+#ifdef HAVE_LIBZSTD
+ union {
+ struct {
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ } zstd;
+ } u;
+#endif
+
};
static void ParseCompressionOption(int compression, CompressionAlgorithm *alg,
@@ -88,6 +100,15 @@ static void WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
static void EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs);
#endif
+#ifdef HAVE_LIBZSTD
+static void InitCompressorZstd(CompressorState *cs, int level);
+static void EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
+static void DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
+static void WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen);
+static void ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF);
+#endif
+
/* Routines that support uncompressed data I/O */
static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
@@ -101,15 +122,25 @@ static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
static void
ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
{
- if (compression == Z_DEFAULT_COMPRESSION ||
- (compression > 0 && compression <= 9))
- *alg = COMPR_ALG_LIBZ;
- else if (compression == 0)
- *alg = COMPR_ALG_NONE;
- else
+ switch (compression)
{
- fatal("invalid compression code: %d", compression);
- *alg = COMPR_ALG_NONE; /* keep compiler quiet */
+#ifdef HAVE_LIBZSTD
+ case ZSTD_COMPRESSION:
+ *alg = COMPR_ALG_ZSTD;
+ break;
+#endif
+#ifdef HAVE_ZLIB
+ case Z_DEFAULT_COMPRESSION:
+ case 1..9:
+ *alg = COMPR_ALG_LIBZ;
+ break;
+#endif
+ case 0:
+ *alg = COMPR_ALG_NONE;
+ break;
+ default:
+ fatal("invalid compression code: %d", compression);
+ *alg = COMPR_ALG_NONE; /* keep compiler quiet */
}
/* The level is just the passed-in value. */
@@ -141,10 +172,23 @@ AllocateCompressor(int compression, WriteFunc writeF)
/*
* Perform compression algorithm specific initialization.
*/
+ switch (alg)
+ {
#ifdef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
+ case COMPR_ALG_LIBZ:
InitCompressorZlib(cs, level);
+ break;
+#endif
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ InitCompressorZstd(cs, level);
+ break;
#endif
+ case COMPR_ALG_NONE:
+ /* Do nothing */
+ break;
+ // default:
+ }
return cs;
}
@@ -162,12 +206,20 @@ ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
if (alg == COMPR_ALG_NONE)
ReadDataFromArchiveNone(AH, readF);
- if (alg == COMPR_ALG_LIBZ)
+ else if (alg == COMPR_ALG_LIBZ)
{
#ifdef HAVE_LIBZ
ReadDataFromArchiveZlib(AH, readF);
#else
fatal("not built with zlib support");
+#endif
+ }
+ else if (alg == COMPR_ALG_ZSTD)
+ {
+#ifdef HAVE_LIBZSTD
+ ReadDataFromArchiveZstd(AH, readF);
+#else
+ fatal("not built with zstd support");
#endif
}
}
@@ -188,6 +240,15 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
fatal("not built with zlib support");
#endif
break;
+
+ case COMPR_ALG_ZSTD:
+#ifdef HAVE_LIBZSTD
+ WriteDataToArchiveZstd(AH, cs, data, dLen);
+#else
+ fatal("not built with zstd support");
+#endif
+ break;
+
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
@@ -204,12 +265,172 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
if (cs->comprAlg == COMPR_ALG_LIBZ)
EndCompressorZlib(AH, cs);
#endif
+#ifdef HAVE_LIBZSTD
+ if (cs->comprAlg == COMPR_ALG_ZSTD)
+ EndCompressorZstd(AH, cs);
+#endif
+
free(cs);
}
/* Private routines, specific to each compression method. */
+// XXX: put in separate files ?
+
+#ifdef HAVE_LIBZSTD
+static void
+InitCompressorZstd(CompressorState *cs, int level)
+{
+ cs->u.zstd.cstream = ZSTD_createCStream();
+ if (cs->u.zstd.cstream == NULL)
+ fatal("could not initialize compression library");
+
+ /* XXX: initialize safely like the corresponding zlib "paranoia" */
+ cs->u.zstd.output.size = ZSTD_CStreamOutSize();
+ cs->u.zstd.output.dst = pg_malloc(cs->u.zstd.output.size);
+ cs->u.zstd.output.pos = 0;
+}
+
+static void
+EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+{
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ for (;;)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ &cs->u.zstd.input, ZSTD_e_end);
+
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ if (res == 0)
+ break;
+
+ if (ZSTD_isError(res))
+ fatal("could not close compression stream: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ // XXX: retval
+ ZSTD_freeCStream(cs->u.zstd.cstream);
+}
+
+static void
+DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+{
+ ZSTD_inBuffer *input = &cs->u.zstd.input;
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ while (input->pos != input->size)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ input, ZSTD_e_continue);
+
+ if (output->pos == output->size ||
+ input->pos != input->size)
+ {
+ /*
+ * Extra paranoia: avoid zero-length chunks, since a zero length
+ * chunk is the EOF marker in the custom format. This should never
+ * happen but...
+ */
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ output->pos = 0;
+ }
+
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ }
+}
+
+static void
+WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen)
+{
+ cs->u.zstd.input.src = (void *) unconstify(char *, data);
+ cs->u.zstd.input.size = dLen;
+ cs->u.zstd.input.pos = 0;
+ DeflateCompressorZstd(AH, cs);
+}
+
+/* Read data from a compressed zstd archive */
+static void
+ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
+{
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ size_t res;
+ size_t input_size;
+
+ dstream = ZSTD_createDStream();
+ if (dstream == NULL)
+ fatal("could not initialize compression library");
+
+ input_size = ZSTD_DStreamInSize();
+ input.src = pg_malloc(input_size);
+
+ output.size = ZSTD_DStreamOutSize();
+ output.dst = pg_malloc(output.size);
+
+ /* read compressed data */
+ for (;;)
+ {
+ size_t cnt;
+
+ input.size = input_size; // XXX: the buffer can grow, we shouldn't keep resetting it to the original value..
+ cnt = readF(AH, (char **)unconstify(void **, &input.src), &input.size);
+ input.pos = 0;
+ input.size = cnt;
+
+ if (cnt == 0)
+ break;
+
+ while (input.pos < input.size)
+ {
+ /* decompress */
+ output.pos = 0;
+ res = ZSTD_decompressStream(dstream, &output, &input);
+
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+
+ /* write to output handle */
+ ((char *)output.dst)[output.pos] = '\0';
+ ahwrite(output.dst, 1, output.pos, AH);
+ }
+ }
+
+ /* write any remainder to output handle */
+ /* XXX: is it needed? */
+ /* If `input.pos < input.size`, some input has not been consumed. */
+ /* But if `output.pos == output.size`, there might be some data left within internal buffers., */
+#if 0
+ while (input.pos < input.size || output.pos == output.size)
+ {
+ output.pos = 0;
+ res = ZSTD_decompressStream(dstream, &output, &input);
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+ ((char *)output.dst)[output.pos] = '\0';
+ ahwrite(output.dst, 1, output.pos, AH);
+ }
+#endif
+
+ pg_free(unconstify(void *, input.src));
+ pg_free(output.dst);
+}
+
+#endif /* HAVE_LIBZSTD */
#ifdef HAVE_LIBZ
+
/*
* Functions for zlib compressed output.
*/
@@ -422,6 +643,19 @@ struct cfp
#ifdef HAVE_LIBZ
gzFile compressedfp;
#endif
+
+#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
+ /* This is a normal file to which we read/write compressed data */
+ struct {
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
+#endif
+
};
#ifdef HAVE_LIBZ
@@ -449,24 +683,25 @@ free_keep_errno(void *p)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_read(const char *path, const char *mode)
+cfopen_read(const char *path, const char *mode, int compression)
{
cfp *fp;
#ifdef HAVE_LIBZ
- if (hasSuffix(path, ".gz"))
- fp = cfopen(path, mode, 1);
+ if (hasSuffix(path, ".gz") || hasSuffix(path, ".zst"))
+ fp = cfopen(path, mode, compression);
else
#endif
{
- fp = cfopen(path, mode, 0);
+ fp = cfopen(path, mode, compression);
#ifdef HAVE_LIBZ
if (fp == NULL)
{
char *fname;
+ char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
- fname = psprintf("%s.gz", path);
- fp = cfopen(fname, mode, 1);
+ fname = psprintf("%s.%s", path, suffix);
+ fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
}
#endif
@@ -491,13 +726,14 @@ cfopen_write(const char *path, const char *mode, int compression)
cfp *fp;
if (compression == 0)
- fp = cfopen(path, mode, 0);
+ fp = cfopen(path, mode, compression);
else
{
-#ifdef HAVE_LIBZ
+#ifdef HAVE_LIBZ // XXX
char *fname;
+ char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
- fname = psprintf("%s.gz", path);
+ fname = psprintf("%s.%s", path, suffix);
fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
#else
@@ -505,11 +741,12 @@ cfopen_write(const char *path, const char *mode, int compression)
fp = NULL; /* keep compiler quiet */
#endif
}
+
return fp;
}
/*
- * Opens file 'path' in 'mode'. If 'compression' is non-zero, the file
+ * Opens file 'path' in 'mode'. If 'alg' is COMPR_ALG_ZLIB, the file
* is opened with libz gzopen(), otherwise with plain fopen().
*
* On failure, return NULL with an error code in errno.
@@ -519,9 +756,19 @@ cfopen(const char *path, const char *mode, int compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
- if (compression != 0)
+ fp->uncompressedfp = NULL;
+#ifdef HAVE_LIBZ
+ fp->compressedfp = NULL;
+#endif
+#ifdef HAVE_LIBZSTD
+ fp->zstd.fp = NULL;
+#endif
+
+ switch (compression)
{
#ifdef HAVE_LIBZ
+ case 1 ... 9: // XXX: nonportable
+ case Z_DEFAULT_COMPRESSION:
if (compression != Z_DEFAULT_COMPRESSION)
{
/* user has specified a compression level, so tell zlib to use it */
@@ -537,30 +784,57 @@ cfopen(const char *path, const char *mode, int compression)
fp->compressedfp = gzopen(path, mode);
}
- fp->uncompressedfp = NULL;
if (fp->compressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
-#else
- fatal("not built with zlib support");
+
+ return fp;
+ break;
#endif
- }
- else
- {
-#ifdef HAVE_LIBZ
- fp->compressedfp = NULL;
+
+#ifdef HAVE_LIBZSTD
+ case ZSTD_COMPRESSION:
+ fp->zstd.fp = fopen(path, mode);
+ // XXX: save the compression params
+ if (fp->zstd.fp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ else if (strchr(mode, 'w'))
+ {
+ fp->zstd.dstream = NULL;
+ fp->zstd.output.size = ZSTD_CStreamOutSize(); // XXX
+ fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
+ fp->zstd.cstream = ZSTD_createCStream();
+ if (fp->zstd.cstream == NULL)
+ fatal("could not initialize compression library");
+ }
+ else if (strchr(mode, 'r'))
+ {
+ fp->zstd.cstream = NULL;
+ fp->zstd.input.size = ZSTD_DStreamOutSize(); // XXX
+ fp->zstd.input.src = pg_malloc0(fp->zstd.input.size);
+ fp->zstd.dstream = ZSTD_createDStream();
+ if (fp->zstd.dstream == NULL)
+ fatal("could not initialize compression library");
+ } // XXX else: bad mode
+ return fp;
+ break;
#endif
+
+ default:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
- }
- return fp;
+ return fp;
+ }
}
@@ -587,6 +861,44 @@ cfread(void *ptr, int size, cfp *fp)
}
else
#endif
+
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t input_size = ZSTD_DStreamInSize();
+ size_t res, cnt;
+
+ output->size = size;
+ output->dst = ptr;
+ output->pos = 0;
+
+ /* read compressed data */
+ while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp)))
+ {
+ input->size = cnt;
+ input->pos = 0;
+
+ for ( ; input->pos < input->size; )
+ {
+ /* decompress */
+ res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ if (res == 0 || output->pos == output->size)
+ break;
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+ }
+
+ if (output->pos == output->size)
+ break; /* We read all the data that fits */
+ }
+
+ ret = output->pos;
+ }
+ else
+#endif
+
{
ret = fread(ptr, 1, size, fp->uncompressedfp);
if (ret != size && !feof(fp->uncompressedfp))
@@ -603,6 +915,35 @@ cfwrite(const void *ptr, int size, cfp *fp)
return gzwrite(fp->compressedfp, ptr, size);
else
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ size_t res, cnt;
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+
+ input->src = ptr;
+ input->size = size;
+ input->pos = 0;
+
+ /* Consume all input, and flush later */
+ while (input->pos != input->size)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ }
+
+ return size;
+ }
+ else
+#endif
+
return fwrite(ptr, 1, size, fp->uncompressedfp);
}
@@ -625,6 +966,20 @@ cfgetc(cfp *fp)
}
else
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ if (cfread(&ret, 1, fp) != 1)
+ {
+ if (feof(fp->zstd.fp))
+ fatal("could not read from input file: end of file");
+ else
+ fatal("could not read from input file: %s", strerror(errno));
+ }
+fprintf(stderr, "cfgetc %d\n", ret);
+ }
+#endif
+
{
ret = fgetc(fp->uncompressedfp);
if (ret == EOF)
@@ -641,6 +996,18 @@ cfgets(cfp *fp, char *buf, int len)
if (fp->compressedfp)
return gzgets(fp->compressedfp, buf, len);
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ int res;
+ res = cfread(buf, len, fp);
+ buf[res] = 0;
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = '\0';
+ return buf;
+ }
+ else
#endif
return fgets(buf, len, fp->uncompressedfp);
}
@@ -662,6 +1029,43 @@ cfclose(cfp *fp)
fp->compressedfp = NULL;
}
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t res, cnt;
+
+ if (fp->zstd.cstream)
+ {
+ for (;;)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ if (res == 0)
+ break;
+ }
+
+ ZSTD_freeCStream(fp->zstd.cstream);
+ pg_free(fp->zstd.output.dst);
+ }
+
+ if (fp->zstd.dstream)
+ {
+ ZSTD_freeDStream(fp->zstd.dstream);
+ pg_free(unconstify(void *, fp->zstd.input.src));
+ }
+
+ result = fclose(fp->zstd.fp);
+ fp->zstd.fp = NULL;
+ }
+ else
#endif
{
result = fclose(fp->uncompressedfp);
@@ -679,6 +1083,11 @@ cfeof(cfp *fp)
if (fp->compressedfp)
return gzeof(fp->compressedfp);
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ return feof(fp->zstd.fp);
+ else
#endif
return feof(fp->uncompressedfp);
}
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index d2e6e1b854..f0ce06f176 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -24,7 +24,8 @@
typedef enum
{
COMPR_ALG_NONE,
- COMPR_ALG_LIBZ
+ COMPR_ALG_LIBZ,
+ COMPR_ALG_ZSTD,
} CompressionAlgorithm;
/* Prototype for callback function to WriteDataToArchive() */
@@ -57,7 +58,7 @@ extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
typedef struct cfp cfp;
extern cfp *cfopen(const char *path, const char *mode, int compression);
-extern cfp *cfopen_read(const char *path, const char *mode);
+extern cfp *cfopen_read(const char *path, const char *mode, int compression);
extern cfp *cfopen_write(const char *path, const char *mode, int compression);
extern int cfread(void *ptr, int size, cfp *fp);
extern int cfwrite(const void *ptr, int size, cfp *fp);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 6a5a22637b..1a229ebedb 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -60,6 +60,11 @@ typedef struct _z_stream
typedef z_stream *z_streamp;
#endif
+#ifdef HAVE_LIBZSTD
+#include <zstd.h>
+#define ZSTD_COMPRESSION -2
+#endif /* HAVE_LIBZSTD */
+
/* Data block types */
#define BLK_DATA 1
#define BLK_BLOBS 3
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 650b542fce..6e55cb425e 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -202,7 +202,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
setFilePath(AH, fname, "toc.dat");
- tocFH = cfopen_read(fname, PG_BINARY_R);
+ tocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
if (tocFH == NULL)
fatal("could not open input file \"%s\": %m", fname);
@@ -388,7 +388,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!filename)
return;
- cfp = cfopen_read(filename, PG_BINARY_R);
+ cfp = cfopen_read(filename, PG_BINARY_R, AH->compression);
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
@@ -440,7 +440,7 @@ _LoadBlobs(ArchiveHandle *AH)
setFilePath(AH, fname, "blobs.toc");
- ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R);
+ ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
if (ctx->blobsTocFH == NULL)
fatal("could not open large object TOC file \"%s\" for input: %m",
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8b1e5cc2b5..f21eb021c7 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -533,7 +533,8 @@ main(int argc, char **argv)
case 'Z': /* Compression Level */
compressLevel = atoi(optarg);
- if (compressLevel < 0 || compressLevel > 9)
+ if ((compressLevel < 0 || compressLevel > 9) &&
+ compressLevel != -2)
{
pg_log_error("compression level must be in range 0..9");
exit_nicely(1);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index de8f838e53..da35415c72 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -346,6 +346,9 @@
/* Define to 1 if you have the `z' library (-lz). */
#undef HAVE_LIBZ
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+#undef HAVE_LIBZSTD
+
/* Define to 1 if you have the `link' function. */
#undef HAVE_LINK
--
2.17.0
0004-cmdline-parser-for-compression-alg-level-opts.patchtext/x-diff; charset=us-asciiDownload
From 81e88ef3a63d1304e14134be14f41c2865c61226 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat, 12 Dec 2020 00:40:39 -0600
Subject: [PATCH 4/7] cmdline parser for compression alg/level/opts
---
src/bin/pg_dump/compress_io.c | 153 +++++++++++---------------
src/bin/pg_dump/compress_io.h | 17 +--
src/bin/pg_dump/core | Bin 0 -> 1257472 bytes
src/bin/pg_dump/kwlookup.c | 1 +
src/bin/pg_dump/pg_backup.h | 19 +++-
src/bin/pg_dump/pg_backup_archiver.c | 46 ++++----
src/bin/pg_dump/pg_backup_archiver.h | 11 +-
src/bin/pg_dump/pg_backup_custom.c | 6 +-
src/bin/pg_dump/pg_backup_directory.c | 19 ++--
src/bin/pg_dump/pg_backup_tar.c | 48 ++++----
src/bin/pg_dump/pg_dump.c | 144 ++++++++++++++++++++----
11 files changed, 278 insertions(+), 186 deletions(-)
create mode 100644 src/bin/pg_dump/core
create mode 120000 src/bin/pg_dump/kwlookup.c
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index b51ba680a2..10db22ff88 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -86,12 +86,9 @@ struct CompressorState
};
-static void ParseCompressionOption(int compression, CompressionAlgorithm *alg,
- int *level);
-
/* Routines that support zlib compressed data I/O */
#ifdef HAVE_LIBZ
-static void InitCompressorZlib(CompressorState *cs, int level);
+static void InitCompressorZlib(CompressorState *cs, Compress *compress);
static void DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs,
bool flush);
static void ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF);
@@ -101,7 +98,7 @@ static void EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs);
#endif
#ifdef HAVE_LIBZSTD
-static void InitCompressorZstd(CompressorState *cs, int level);
+static void InitCompressorZstd(CompressorState *cs, Compress *compress);
static void EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
static void DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
static void WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
@@ -114,80 +111,40 @@ static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen);
-/*
- * Interprets a numeric 'compression' value. The algorithm implied by the
- * value (zlib or none at the moment), is returned in *alg, and the
- * zlib compression level in *level.
- */
-static void
-ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
-{
- switch (compression)
- {
-#ifdef HAVE_LIBZSTD
- case ZSTD_COMPRESSION:
- *alg = COMPR_ALG_ZSTD;
- break;
-#endif
-#ifdef HAVE_ZLIB
- case Z_DEFAULT_COMPRESSION:
- case 1..9:
- *alg = COMPR_ALG_LIBZ;
- break;
-#endif
- case 0:
- *alg = COMPR_ALG_NONE;
- break;
- default:
- fatal("invalid compression code: %d", compression);
- *alg = COMPR_ALG_NONE; /* keep compiler quiet */
- }
-
- /* The level is just the passed-in value. */
- if (level)
- *level = compression;
-}
-
/* Public interface routines */
/* Allocate a new compressor */
CompressorState *
-AllocateCompressor(int compression, WriteFunc writeF)
+AllocateCompressor(Compress *compression, WriteFunc writeF)
{
CompressorState *cs;
- CompressionAlgorithm alg;
- int level;
-
- ParseCompressionOption(compression, &alg, &level);
-
-#ifndef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
- fatal("not built with zlib support");
-#endif
cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
cs->writeF = writeF;
- cs->comprAlg = alg;
+ cs->comprAlg = compression->alg;
/*
* Perform compression algorithm specific initialization.
*/
- switch (alg)
+ Assert (compression->alg != COMPR_ALG_DEFAULT);
+ switch (compression->alg)
{
#ifdef HAVE_LIBZ
case COMPR_ALG_LIBZ:
- InitCompressorZlib(cs, level);
+ InitCompressorZlib(cs, compression);
break;
#endif
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- InitCompressorZstd(cs, level);
+ InitCompressorZstd(cs, compression);
break;
#endif
case COMPR_ALG_NONE:
/* Do nothing */
break;
- // default:
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
return cs;
@@ -198,12 +155,9 @@ AllocateCompressor(int compression, WriteFunc writeF)
* out with ahwrite().
*/
void
-ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
+ReadDataFromArchive(ArchiveHandle *AH, ReadFunc readF)
{
- CompressionAlgorithm alg;
-
- ParseCompressionOption(compression, &alg, NULL);
-
+ CompressionAlgorithm alg = AH->compression.alg;
if (alg == COMPR_ALG_NONE)
ReadDataFromArchiveNone(AH, readF);
else if (alg == COMPR_ALG_LIBZ)
@@ -233,25 +187,25 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
{
switch (cs->comprAlg)
{
- case COMPR_ALG_LIBZ:
#ifdef HAVE_LIBZ
+ case COMPR_ALG_LIBZ:
WriteDataToArchiveZlib(AH, cs, data, dLen);
-#else
- fatal("not built with zlib support");
-#endif
break;
+#endif
- case COMPR_ALG_ZSTD:
#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
WriteDataToArchiveZstd(AH, cs, data, dLen);
-#else
- fatal("not built with zstd support");
-#endif
break;
+#endif
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
+
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -278,9 +232,30 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
#ifdef HAVE_LIBZSTD
static void
-InitCompressorZstd(CompressorState *cs, int level)
+InitCompressorZstd(CompressorState *cs, Compress *compress)
{
+ size_t res;
+
cs->u.zstd.cstream = ZSTD_createCStream();
+
+ if (compress->level != 0) // XXX
+ {
+ res = ZSTD_CCtx_setParameter(cs->u.zstd.cstream,
+ ZSTD_c_compressionLevel, compress->level);
+ if (ZSTD_isError(res))
+ fatal("could not set compression level: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ if (compress->zstdlong)
+ {
+ res = ZSTD_CCtx_setParameter(cs->u.zstd.cstream,
+ ZSTD_c_enableLongDistanceMatching, 1); // XXX: allow to disable it
+ if (ZSTD_isError(res))
+ fatal("could not set compression parameter: %s",
+ ZSTD_getErrorName(res));
+ }
+
if (cs->u.zstd.cstream == NULL)
fatal("could not initialize compression library");
@@ -436,7 +411,7 @@ ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
*/
static void
-InitCompressorZlib(CompressorState *cs, int level)
+InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
@@ -453,7 +428,7 @@ InitCompressorZlib(CompressorState *cs, int level)
cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
cs->zlibOutSize = ZLIB_OUT_SIZE;
- if (deflateInit(zp, level) != Z_OK)
+ if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
@@ -683,7 +658,7 @@ free_keep_errno(void *p)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_read(const char *path, const char *mode, int compression)
+cfopen_read(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
@@ -698,7 +673,7 @@ cfopen_read(const char *path, const char *mode, int compression)
if (fp == NULL)
{
char *fname;
- char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
+ char *suffix = compression->alg == COMPR_ALG_ZSTD ? "zst" : "gz";
fname = psprintf("%s.%s", path, suffix);
fp = cfopen(fname, mode, compression);
@@ -714,24 +689,23 @@ cfopen_read(const char *path, const char *mode, int compression)
* be a filemode as accepted by fopen() and gzopen() that indicates writing
* ("w", "wb", "a", or "ab").
*
- * If 'compression' is non-zero, a gzip compressed stream is opened, and
- * 'compression' indicates the compression level used. The ".gz" suffix
- * is automatically added to 'path' in that case.
+ * Use compression if specified.
+ * The appropriate suffix is automatically added to 'path' in that case.
*
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_write(const char *path, const char *mode, int compression)
+cfopen_write(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
- if (compression == 0)
+ if (compression->alg == COMPR_ALG_NONE)
fp = cfopen(path, mode, compression);
else
{
#ifdef HAVE_LIBZ // XXX
char *fname;
- char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
+ char *suffix = compression->alg == COMPR_ALG_ZSTD ? "zst" : "gz";
fname = psprintf("%s.%s", path, suffix);
fp = cfopen(fname, mode, compression);
@@ -752,7 +726,7 @@ cfopen_write(const char *path, const char *mode, int compression)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen(const char *path, const char *mode, int compression)
+cfopen(const char *path, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
@@ -764,18 +738,17 @@ cfopen(const char *path, const char *mode, int compression)
fp->zstd.fp = NULL;
#endif
- switch (compression)
+ switch (compression->alg)
{
#ifdef HAVE_LIBZ
- case 1 ... 9: // XXX: nonportable
- case Z_DEFAULT_COMPRESSION:
- if (compression != Z_DEFAULT_COMPRESSION)
+ case COMPR_ALG_LIBZ:
+ if (compression->level != Z_DEFAULT_COMPRESSION)
{
/* user has specified a compression level, so tell zlib to use it */
char mode_compression[32];
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
- mode, compression);
+ mode, compression->level);
fp->compressedfp = gzopen(path, mode_compression);
}
else
@@ -795,7 +768,7 @@ cfopen(const char *path, const char *mode, int compression)
#endif
#ifdef HAVE_LIBZSTD
- case ZSTD_COMPRESSION:
+ case COMPR_ALG_ZSTD:
fp->zstd.fp = fopen(path, mode);
// XXX: save the compression params
if (fp->zstd.fp == NULL)
@@ -825,15 +798,19 @@ cfopen(const char *path, const char *mode, int compression)
break;
#endif
- default:
+ case COMPR_ALG_NONE:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
-
return fp;
+ break;
+
+ default:
+ /* should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -1005,7 +982,7 @@ cfgets(cfp *fp, char *buf, int len)
buf[res] = 0;
if (strchr(buf, '\n'))
*strchr(buf, '\n') = '\0';
- return buf;
+ return res > 0 ? buf : 0;
}
else
#endif
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index f0ce06f176..ae37be08d9 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -21,13 +21,6 @@
#define ZLIB_OUT_SIZE 4096
#define ZLIB_IN_SIZE 4096
-typedef enum
-{
- COMPR_ALG_NONE,
- COMPR_ALG_LIBZ,
- COMPR_ALG_ZSTD,
-} CompressionAlgorithm;
-
/* Prototype for callback function to WriteDataToArchive() */
typedef void (*WriteFunc) (ArchiveHandle *AH, const char *buf, size_t len);
@@ -47,8 +40,8 @@ typedef size_t (*ReadFunc) (ArchiveHandle *AH, char **buf, size_t *buflen);
/* struct definition appears in compress_io.c */
typedef struct CompressorState CompressorState;
-extern CompressorState *AllocateCompressor(int compression, WriteFunc writeF);
-extern void ReadDataFromArchive(ArchiveHandle *AH, int compression,
+extern CompressorState *AllocateCompressor(Compress *compression, WriteFunc writeF);
+extern void ReadDataFromArchive(ArchiveHandle *AH,
ReadFunc readF);
extern void WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
const void *data, size_t dLen);
@@ -57,9 +50,9 @@ extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
typedef struct cfp cfp;
-extern cfp *cfopen(const char *path, const char *mode, int compression);
-extern cfp *cfopen_read(const char *path, const char *mode, int compression);
-extern cfp *cfopen_write(const char *path, const char *mode, int compression);
+extern cfp *cfopen(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_read(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_write(const char *path, const char *mode, Compress *compression);
extern int cfread(void *ptr, int size, cfp *fp);
extern int cfwrite(const void *ptr, int size, cfp *fp);
extern int cfgetc(cfp *fp);
diff --git a/src/bin/pg_dump/core b/src/bin/pg_dump/core
new file mode 100644
index 0000000000000000000000000000000000000000..bdb556ec717e50744ccc2a7256ba9f90cb41427c
GIT binary patch
literal 1257472
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≤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”+>_^<&alvNe)y9eb`JTXGDqBwJcA
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⋘`!fyt?YpMv_7e^IkISH5zkz<p
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
literal 0
HcmV?d00001
diff --git a/src/bin/pg_dump/kwlookup.c b/src/bin/pg_dump/kwlookup.c
new file mode 120000
index 0000000000..73c3abc1c7
--- /dev/null
+++ b/src/bin/pg_dump/kwlookup.c
@@ -0,0 +1 @@
+../../../src/backend/parser/kwlookup.c
\ No newline at end of file
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 9d0056a569..00d77ac16a 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -72,6 +72,21 @@ typedef struct _connParams
char *override_dbname;
} ConnParams;
+typedef enum
+{
+ COMPR_ALG_DEFAULT = -1,
+ COMPR_ALG_NONE,
+ COMPR_ALG_LIBZ,
+ COMPR_ALG_ZSTD,
+} CompressionAlgorithm;
+
+typedef struct Compress {
+ CompressionAlgorithm alg;
+ int level;
+ bool zstdlong;
+} Compress;
+
+
typedef struct _restoreOptions
{
int createDB; /* Issue commands to create the database */
@@ -125,7 +140,7 @@ typedef struct _restoreOptions
int noDataForFailedTables;
int exit_on_error;
- int compression;
+ Compress compression;
int suppressDumpWarnings; /* Suppress output of WARNING entries
* to stderr */
bool single_txn;
@@ -281,7 +296,7 @@ extern Archive *OpenArchive(const char *FileSpec, const ArchiveFormat fmt);
/* Create a new archive */
extern Archive *CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker);
/* The --list option */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1f82c6499b..be2b0882d9 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -70,7 +70,7 @@ typedef struct _parallelReadyList
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr);
static void _getObjectDescription(PQExpBuffer buf, TocEntry *te);
static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData);
@@ -98,7 +98,7 @@ static int _discoverArchiveFormat(ArchiveHandle *AH);
static int RestoringToDB(ArchiveHandle *AH);
static void dump_lo_buf(ArchiveHandle *AH);
static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
-static void SetOutput(ArchiveHandle *AH, const char *filename, int compression);
+static void SetOutput(ArchiveHandle *AH, const char *filename, Compress *compress);
static OutputContext SaveOutput(ArchiveHandle *AH);
static void RestoreOutput(ArchiveHandle *AH, OutputContext savedContext);
@@ -238,7 +238,7 @@ setupRestoreWorker(Archive *AHX)
/* Public */
Archive *
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker)
{
@@ -253,7 +253,9 @@ CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
Archive *
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
{
- ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, true, archModeRead, setupRestoreWorker);
+ Compress compress = {0};
+ ArchiveHandle *AH = _allocAH(FileSpec, fmt, &compress, true, archModeRead,
+ setupRestoreWorker);
return (Archive *) AH;
}
@@ -382,7 +384,7 @@ RestoreArchive(Archive *AHX)
* Make sure we won't need (de)compression we haven't got
*/
#ifndef HAVE_LIBZ
- if (AH->compression != 0 && AH->PrintTocDataPtr != NULL)
+ if (AH->compression.alg != COMPR_ALG_NONE && AH->PrintTocDataPtr != NULL)
{
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
@@ -457,8 +459,8 @@ RestoreArchive(Archive *AHX)
* Setup the output file if necessary.
*/
sav = SaveOutput(AH);
- if (ropt->filename || ropt->compression)
- SetOutput(AH, ropt->filename, ropt->compression);
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
+ SetOutput(AH, ropt->filename, &ropt->compression);
ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
@@ -738,7 +740,7 @@ RestoreArchive(Archive *AHX)
*/
AH->stage = STAGE_FINALIZING;
- if (ropt->filename || ropt->compression)
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
RestoreOutput(AH, sav);
if (ropt->useDB)
@@ -1121,10 +1123,11 @@ PrintTOCSummary(Archive *AHX)
OutputContext sav;
const char *fmtName;
char stamp_str[64];
+ Compress nocompression = {0};
sav = SaveOutput(AH);
if (ropt->filename)
- SetOutput(AH, ropt->filename, 0 /* no compression */ );
+ SetOutput(AH, ropt->filename, &nocompression);
if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
localtime(&AH->createDate)) == 0)
@@ -1133,7 +1136,7 @@ PrintTOCSummary(Archive *AHX)
ahprintf(AH, ";\n; Archive created at %s\n", stamp_str);
ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %d\n",
sanitize_line(AH->archdbname, false),
- AH->tocCount, AH->compression);
+ AH->tocCount, AH->compression.alg);
switch (AH->format)
{
@@ -1487,7 +1490,7 @@ archprintf(Archive *AH, const char *fmt,...)
*******************************/
static void
-SetOutput(ArchiveHandle *AH, const char *filename, int compression)
+SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
{
int fn;
@@ -1510,12 +1513,12 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
/* If compression explicitly requested, use gzopen */
#ifdef HAVE_LIBZ
- if (compression != 0)
+ if (compression->alg != COMPR_ALG_NONE)
{
char fmode[14];
/* Don't use PG_BINARY_x since this is zlib */
- sprintf(fmode, "wb%d", compression);
+ sprintf(fmode, "wb%d", compression->level);
if (fn >= 0)
AH->OF = gzdopen(dup(fn), fmode);
else
@@ -2259,7 +2262,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
*/
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr)
{
ArchiveHandle *AH;
@@ -2310,9 +2313,8 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
AH->toc->prev = AH->toc;
AH->mode = mode;
- AH->compression = compression;
+ AH->compression = *compression;
AH->dosync = dosync;
-
memset(&(AH->sqlparse), 0, sizeof(AH->sqlparse));
/* Open stdout with no compression for AH output handle */
@@ -2325,7 +2327,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
* Force stdin/stdout into binary mode if that is what we are using.
*/
#ifdef WIN32
- if ((fmt != archNull || compression != 0) &&
+ if ((fmt != archNull || compression->alg != COMPR_ALG_NONE) &&
(AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0))
{
if (mode == archModeWrite)
@@ -3741,7 +3743,7 @@ WriteHead(ArchiveHandle *AH)
AH->WriteBytePtr(AH, AH->intSize);
AH->WriteBytePtr(AH, AH->offSize);
AH->WriteBytePtr(AH, AH->format);
- WriteInt(AH, AH->compression);
+ WriteInt(AH, AH->compression.alg);
crtm = *localtime(&AH->createDate);
WriteInt(AH, crtm.tm_sec);
WriteInt(AH, crtm.tm_min);
@@ -3816,15 +3818,15 @@ ReadHead(ArchiveHandle *AH)
if (AH->version >= K_VERS_1_2)
{
if (AH->version < K_VERS_1_4)
- AH->compression = AH->ReadBytePtr(AH);
+ AH->compression.alg = AH->ReadBytePtr(AH);
else
- AH->compression = ReadInt(AH);
+ AH->compression.alg = ReadInt(AH);
}
else
- AH->compression = Z_DEFAULT_COMPRESSION;
+ AH->compression.alg = Z_DEFAULT_COMPRESSION;
#ifndef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
pg_log_warning("archive is compressed, but this installation does not support compression -- no data will be available");
#endif
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 1a229ebedb..641ba2d043 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -47,8 +47,6 @@
#define GZWRITE(p, s, n, fh) (fwrite(p, s, n, fh) * (s))
#define GZREAD(p, s, n, fh) fread(p, s, n, fh)
#define GZEOF(fh) feof(fh)
-/* this is just the redefinition of a libz constant */
-#define Z_DEFAULT_COMPRESSION (-1)
typedef struct _z_stream
{
@@ -62,7 +60,6 @@ typedef z_stream *z_streamp;
#ifdef HAVE_LIBZSTD
#include <zstd.h>
-#define ZSTD_COMPRESSION -2
#endif /* HAVE_LIBZSTD */
/* Data block types */
@@ -334,13 +331,7 @@ struct _archiveHandle
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
- int compression; /*---------
- * Compression requested on open
- * Possible values for compression:
- * -2 ZSTD_COMPRESSION
- * -1 Z_DEFAULT_COMPRESSION
- * 0 COMPRESSION_NONE
- * 1-9 levels for gzip compression */
+ Compress compression; /* Compression requested on open */
bool dosync; /* data requested to be synced on sight */
ArchiveMode mode; /* File mode - r or w */
void *formatData; /* Header data specific to file format */
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..55a887a236 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -298,7 +298,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
_WriteByte(AH, BLK_DATA); /* Block type */
WriteInt(AH, te->dumpId); /* For sanity check */
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -377,7 +377,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
WriteInt(AH, oid);
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -566,7 +566,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
static void
_PrintData(ArchiveHandle *AH)
{
- ReadDataFromArchive(AH, AH->compression, _CustomReadFunc);
+ ReadDataFromArchive(AH, _CustomReadFunc);
}
static void
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 6e55cb425e..dfa36d5dc1 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -202,7 +202,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
setFilePath(AH, fname, "toc.dat");
- tocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
+ tocFH = cfopen_read(fname, PG_BINARY_R, &AH->compression);
if (tocFH == NULL)
fatal("could not open input file \"%s\": %m", fname);
@@ -327,7 +327,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
setFilePath(AH, fname, tctx->filename);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -388,12 +388,12 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!filename)
return;
- cfp = cfopen_read(filename, PG_BINARY_R, AH->compression);
+ cfp = cfopen_read(filename, PG_BINARY_R, &AH->compression);
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
- buf = pg_malloc(ZLIB_OUT_SIZE);
+ buf = pg_malloc(ZLIB_OUT_SIZE); // XXX
buflen = ZLIB_OUT_SIZE;
while ((cnt = cfread(buf, buflen, cfp)))
@@ -435,12 +435,13 @@ _LoadBlobs(ArchiveHandle *AH)
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
char line[MAXPGPATH];
+ Compress nocompression = {0};
StartRestoreBlobs(AH);
setFilePath(AH, fname, "blobs.toc");
- ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
+ ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open large object TOC file \"%s\" for input: %m",
@@ -573,6 +574,7 @@ _CloseArchive(ArchiveHandle *AH)
{
cfp *tocFH;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "toc.dat");
@@ -580,7 +582,7 @@ _CloseArchive(ArchiveHandle *AH)
ctx->pstate = ParallelBackupStart(AH);
/* The TOC is always created uncompressed */
- tocFH = cfopen_write(fname, PG_BINARY_W, 0);
+ tocFH = cfopen_write(fname, PG_BINARY_W, &nocompression);
if (tocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
ctx->dataFH = tocFH;
@@ -639,11 +641,12 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
{
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "blobs.toc");
/* The blob TOC file is never compressed */
- ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
+ ctx->blobsTocFH = cfopen_write(fname, "ab", &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -661,7 +664,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 54e708875c..79e528ea89 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -196,10 +196,10 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
/*
* We don't support compression because reading the files back is not
- * possible since gzdopen uses buffered IO which totally screws file
+ * possible since gzdopen uses buffered IO which totally screws file XXX
* positioning.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
fatal("compression is not supported by tar archive format");
}
else
@@ -241,6 +241,22 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
}
}
+/*
+ * Return a string for the given AH's compression.
+ * The string is statically allocated.
+ */
+static const char *
+compress_suffix(ArchiveHandle *AH)
+{
+ CompressionAlgorithm alg;
+ alg = AH->compression.alg;
+ if (alg == COMPR_ALG_LIBZ)
+ return ".gz";
+ if (alg == COMPR_ALG_ZSTD)
+ return ".zstd";
+ return "";
+}
+
/*
* - Start a new TOC entry
* Setup the output file name.
@@ -254,14 +270,8 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
if (te->dataDumper != NULL)
{
-#ifdef HAVE_LIBZ
- if (AH->compression == 0)
- sprintf(fn, "%d.dat", te->dumpId);
- else
- sprintf(fn, "%d.dat.gz", te->dumpId);
-#else
- sprintf(fn, "%d.dat", te->dumpId);
-#endif
+ const char *suffix = compress_suffix(AH);
+ sprintf(fn, "%d.dat%s", te->dumpId, suffix);
ctx->filename = pg_strdup(fn);
}
else
@@ -352,7 +362,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression == 0)
+ if (AH->compression.alg == COMPR_ALG_NONE)
tm->nFH = ctx->tarFH;
else
fatal("compression is not supported by tar archive format");
@@ -413,9 +423,9 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
{
- sprintf(fmode, "wb%d", AH->compression);
+ sprintf(fmode, "wb%d", AH->compression.level);
tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
if (tm->zFH == NULL)
fatal("could not open temporary file");
@@ -443,7 +453,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
/*
* Close the GZ file since we dup'd. This will flush the buffers.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
if (GZCLOSE(th->zFH) != 0)
fatal("could not close tar member");
@@ -868,7 +878,7 @@ _CloseArchive(ArchiveHandle *AH)
memcpy(ropt, AH->public.ropt, sizeof(RestoreOptions));
ropt->filename = NULL;
ropt->dropSchema = 1;
- ropt->compression = 0;
+ ropt->compression.alg = COMPR_ALG_NONE;
ropt->superuser = NULL;
ropt->suppressDumpWarnings = true;
@@ -952,16 +962,12 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
char fname[255];
- char *sfx;
+ const char *sfx;
if (oid == 0)
fatal("invalid OID for large object (%u)", oid);
- if (AH->compression != 0)
- sfx = ".gz";
- else
- sfx = "";
-
+ sfx = compress_suffix(AH);
sprintf(fname, "blob_%u.dat%s", oid, sfx);
tarPrintf(ctx->blobToc, "%u %s\n", oid, fname);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f21eb021c7..c8662f731b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -59,6 +59,7 @@
#include "getopt_long.h"
#include "libpq/libpq-fs.h"
#include "parallel.h"
+#include "compress_io.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "pg_dump.h"
@@ -297,6 +298,99 @@ static void setupDumpWorker(Archive *AHX);
static TableInfo *getRootTableInfo(TableInfo *tbinfo);
+/* Parse the string into compression options */
+static void
+parse_compression(const char *optarg, Compress *compress)
+{
+ if (optarg[0] == '0' && optarg[1] == '\0')
+ compress->alg = COMPR_ALG_NONE;
+ else if ((optarg[0] > '0' && optarg[0] <= '9') ||
+ optarg[0] == '-')
+ {
+ compress->alg = COMPR_ALG_LIBZ;
+ compress->level = atoi(optarg);
+ if (optarg[1] != '\0')
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+ }
+ else
+ {
+ /* Parse a more flexible string like level=3 alg=zlib opts=long */
+ for (;;)
+ {
+ char *eq = strchr(optarg, '=');
+ int len;
+
+ if (eq == NULL)
+ {
+ pg_log_error("compression options must be key=value: %s", optarg);
+ exit_nicely(1);
+ }
+
+ len = eq - optarg;
+ if (strncmp(optarg, "alg", len) == 0)
+ {
+ if (strchr(eq, ' '))
+ len = strchr(eq, ' ') - eq - 1;
+ else
+ len = strlen(eq) - len;
+ if (strncmp(1+eq, "zlib", len) == 0 ||
+ strncmp(1+eq, "libz", len) == 0)
+ compress->alg = COMPR_ALG_LIBZ;
+ else if (strncmp(1+eq, "zstd", len) == 0)
+ compress->alg = COMPR_ALG_ZSTD;
+ else
+ {
+ pg_log_error("unknown compression algorithm: %s", 1+eq);
+ exit_nicely(1);
+ }
+ }
+ else if (strncmp(optarg, "level", len) == 0)
+ compress->level = atoi(1+eq);
+ else if (strncmp(optarg, "opt", len) == 0)
+ {
+ if (strchr(eq, ' '))
+ len = strchr(eq, ' ') - eq - 1;
+ else
+ len = strlen(eq) - len;
+ if (strncmp(1+eq, "zstdlong", len) == 0)
+ compress->zstdlong = true;
+ else
+ {
+ pg_log_error("unknown compression option: %s", 1+eq);
+ exit_nicely(1);
+ }
+ }
+ else
+ {
+ pg_log_error("unknown compression setting: %s", optarg);
+ exit_nicely(1);
+ }
+
+ optarg = strchr(eq, ' ');
+ if (!optarg++)
+ break;
+ }
+
+ /* zstd will check its own compression level later */
+ if (compress->alg != COMPR_ALG_ZSTD)
+ {
+ if (compress->level < 0 || compress->level > 9)
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+ if (compress->zstdlong)
+ {
+ pg_log_error("compression option not supported with this algorithm");
+ exit_nicely(1);
+ }
+ }
+ }
+}
+
int
main(int argc, char **argv)
{
@@ -319,7 +413,7 @@ main(int argc, char **argv)
char *use_role = NULL;
long rowsPerInsert;
int numWorkers = 1;
- int compressLevel = -1;
+ Compress compress = { .alg = COMPR_ALG_DEFAULT };
int plainText = 0;
ArchiveFormat archiveFormat = archUnknown;
ArchiveMode archiveMode;
@@ -532,13 +626,7 @@ main(int argc, char **argv)
break;
case 'Z': /* Compression Level */
- compressLevel = atoi(optarg);
- if ((compressLevel < 0 || compressLevel > 9) &&
- compressLevel != -2)
- {
- pg_log_error("compression level must be in range 0..9");
- exit_nicely(1);
- }
+ parse_compression(optarg, &compress);
break;
case 0:
@@ -680,20 +768,39 @@ main(int argc, char **argv)
plainText = 1;
/* Custom and directory formats are compressed by default, others not */
- if (compressLevel == -1)
+ if (compress.alg == COMPR_ALG_DEFAULT)
{
-#ifdef HAVE_LIBZ
if (archiveFormat == archCustom || archiveFormat == archDirectory)
- compressLevel = Z_DEFAULT_COMPRESSION;
- else
+ {
+#ifdef HAVE_LIBZ
+ compress.alg = COMPR_ALG_LIBZ;
#endif
- compressLevel = 0;
+#ifdef HAVE_LIBZSTD
+ compress.alg = COMPR_ALG_ZSTD; // Set default for testing purposes
+#endif
+ compress.level = 3;
+ }
+ else
+ {
+ compress.alg = COMPR_ALG_NONE;
+ compress.level = 0;
+ }
}
#ifndef HAVE_LIBZ
- if (compressLevel != 0)
+ if (compress.alg == COMPR_ALG_LIBZ)
+ {
pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
- compressLevel = 0;
+ compress.alg = 0;
+ }
+#endif
+
+#ifndef HAVE_LIBZ
+ if (compress.alg == COMPR_ALG_ZSTD)
+ {
+ pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
+ compress.alg = 0;
+ }
#endif
/*
@@ -724,7 +831,7 @@ main(int argc, char **argv)
fatal("option --index-collation-versions-unknown only works in binary upgrade mode");
/* Open the output file */
- fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
+ fout = CreateArchive(filename, archiveFormat, &compress, dosync,
archiveMode, setupDumpWorker);
/* Make dump options accessible right away */
@@ -958,10 +1065,7 @@ main(int argc, char **argv)
ropt->sequence_data = dopt.sequence_data;
ropt->binary_upgrade = dopt.binary_upgrade;
- if (compressLevel == -1)
- ropt->compression = 0;
- else
- ropt->compression = compressLevel;
+ ropt->compression = compress;
ropt->suppressDumpWarnings = true; /* We've already shown them */
--
2.17.0
0005-union-with-a-CompressionAlgorithm-alg.patchtext/x-diff; charset=us-asciiDownload
From c9d537cd341c96f21d749006002291e30a383463 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 21 Dec 2020 00:11:43 -0600
Subject: [PATCH 5/7] union with a CompressionAlgorithm alg
---
src/bin/pg_dump/compress_io.c | 219 ++++++++++++++++------------------
1 file changed, 106 insertions(+), 113 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 10db22ff88..ae1ae7b0d0 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -614,22 +614,27 @@ WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
*/
struct cfp
{
- FILE *uncompressedfp;
+ CompressionAlgorithm alg;
+
+ union {
+ FILE *fp;
+
#ifdef HAVE_LIBZ
- gzFile compressedfp;
+ gzFile gzfp;
#endif
-#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
- /* This is a normal file to which we read/write compressed data */
- struct {
- FILE *fp;
- // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
- ZSTD_DStream *dstream;
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
- } zstd;
+#ifdef HAVE_LIBZSTD
+ struct {
+ /* This is a normal file to which we read/write compressed data */
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
#endif
+ } u;
};
@@ -730,13 +735,7 @@ cfopen(const char *path, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
- fp->uncompressedfp = NULL;
-#ifdef HAVE_LIBZ
- fp->compressedfp = NULL;
-#endif
-#ifdef HAVE_LIBZSTD
- fp->zstd.fp = NULL;
-#endif
+ fp->alg = compression->alg;
switch (compression->alg)
{
@@ -749,15 +748,15 @@ cfopen(const char *path, const char *mode, Compress *compression)
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
mode, compression->level);
- fp->compressedfp = gzopen(path, mode_compression);
+ fp->u.gzfp = gzopen(path, mode_compression);
}
else
{
/* don't specify a level, just use the zlib default */
- fp->compressedfp = gzopen(path, mode);
+ fp->u.gzfp = gzopen(path, mode);
}
- if (fp->compressedfp == NULL)
+ if (fp->u.gzfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -769,29 +768,29 @@ cfopen(const char *path, const char *mode, Compress *compression)
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- fp->zstd.fp = fopen(path, mode);
+ fp->u.zstd.fp = fopen(path, mode);
// XXX: save the compression params
- if (fp->zstd.fp == NULL)
+ if (fp->u.zstd.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
else if (strchr(mode, 'w'))
{
- fp->zstd.dstream = NULL;
- fp->zstd.output.size = ZSTD_CStreamOutSize(); // XXX
- fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
- fp->zstd.cstream = ZSTD_createCStream();
- if (fp->zstd.cstream == NULL)
+ fp->u.zstd.dstream = NULL;
+ fp->u.zstd.output.size = ZSTD_CStreamOutSize(); // XXX
+ fp->u.zstd.output.dst = pg_malloc0(fp->u.zstd.output.size);
+ fp->u.zstd.cstream = ZSTD_createCStream();
+ if (fp->u.zstd.cstream == NULL)
fatal("could not initialize compression library");
}
else if (strchr(mode, 'r'))
{
- fp->zstd.cstream = NULL;
- fp->zstd.input.size = ZSTD_DStreamOutSize(); // XXX
- fp->zstd.input.src = pg_malloc0(fp->zstd.input.size);
- fp->zstd.dstream = ZSTD_createDStream();
- if (fp->zstd.dstream == NULL)
+ fp->u.zstd.cstream = NULL;
+ fp->u.zstd.input.size = ZSTD_DStreamOutSize(); // XXX
+ fp->u.zstd.input.src = pg_malloc0(fp->u.zstd.input.size);
+ fp->u.zstd.dstream = ZSTD_createDStream();
+ if (fp->u.zstd.dstream == NULL)
fatal("could not initialize compression library");
} // XXX else: bad mode
return fp;
@@ -799,8 +798,8 @@ cfopen(const char *path, const char *mode, Compress *compression)
#endif
case COMPR_ALG_NONE:
- fp->uncompressedfp = fopen(path, mode);
- if (fp->uncompressedfp == NULL)
+ fp->u.fp = fopen(path, mode);
+ if (fp->u.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -824,26 +823,26 @@ cfread(void *ptr, int size, cfp *fp)
return 0;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzread(fp->compressedfp, ptr, size);
- if (ret != size && !gzeof(fp->compressedfp))
+ ret = gzread(fp->u.gzfp, ptr, size);
+ if (ret != size && !gzeof(fp->u.gzfp))
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
fatal("could not read from input file: %s",
errnum == Z_ERRNO ? strerror(errno) : errmsg);
}
+ return ret;
}
- else
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t input_size = ZSTD_DStreamInSize();
size_t res, cnt;
@@ -852,7 +851,7 @@ cfread(void *ptr, int size, cfp *fp)
output->pos = 0;
/* read compressed data */
- while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp)))
+ while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->u.zstd.fp)))
{
input->size = cnt;
input->pos = 0;
@@ -860,7 +859,7 @@ cfread(void *ptr, int size, cfp *fp)
for ( ; input->pos < input->size; )
{
/* decompress */
- res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ res = ZSTD_decompressStream(fp->u.zstd.dstream, output, input);
if (res == 0 || output->pos == output->size)
break;
if (ZSTD_isError(res))
@@ -871,16 +870,13 @@ cfread(void *ptr, int size, cfp *fp)
break; /* We read all the data that fits */
}
- ret = output->pos;
+ return output->pos;
}
- else
#endif
- {
- ret = fread(ptr, 1, size, fp->uncompressedfp);
- if (ret != size && !feof(fp->uncompressedfp))
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
+ ret = fread(ptr, 1, size, fp->u.fp);
+ if (ret != size && !feof(fp->u.fp))
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -888,16 +884,16 @@ int
cfwrite(const void *ptr, int size, cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzwrite(fp->compressedfp, ptr, size);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzwrite(fp->u.gzfp, ptr, size);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
size_t res, cnt;
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
input->src = ptr;
input->size = size;
@@ -907,21 +903,20 @@ cfwrite(const void *ptr, int size, cfp *fp)
while (input->pos != input->size)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_continue);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
}
return size;
}
- else
#endif
- return fwrite(ptr, 1, size, fp->uncompressedfp);
+ return fwrite(ptr, 1, size, fp->u.fp);
}
int
@@ -930,39 +925,38 @@ cfgetc(cfp *fp)
int ret;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzgetc(fp->compressedfp);
+ ret = gzgetc(fp->u.gzfp);
if (ret == EOF)
{
- if (!gzeof(fp->compressedfp))
+ if (!gzeof(fp->u.gzfp))
fatal("could not read from input file: %s", strerror(errno));
else
fatal("could not read from input file: end of file");
}
+ return ret;
}
- else
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
if (cfread(&ret, 1, fp) != 1)
{
- if (feof(fp->zstd.fp))
+ if (feof(fp->u.zstd.fp))
fatal("could not read from input file: end of file");
else
fatal("could not read from input file: %s", strerror(errno));
}
fprintf(stderr, "cfgetc %d\n", ret);
+ return ret;
}
#endif
- {
- ret = fgetc(fp->uncompressedfp);
- if (ret == EOF)
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
-
+ ret = fgetc(fp->u.fp);
+ if (ret == EOF)
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -970,12 +964,12 @@ char *
cfgets(cfp *fp, char *buf, int len)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzgets(fp->compressedfp, buf, len);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzgets(fp->u.gzfp, buf, len);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
int res;
res = cfread(buf, len, fp);
@@ -984,9 +978,9 @@ cfgets(cfp *fp, char *buf, int len)
*strchr(buf, '\n') = '\0';
return res > 0 ? buf : 0;
}
- else
#endif
- return fgets(buf, len, fp->uncompressedfp);
+
+ return fgets(buf, len, fp->u.fp);
}
int
@@ -1000,56 +994,55 @@ cfclose(cfp *fp)
return EOF;
}
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- result = gzclose(fp->compressedfp);
- fp->compressedfp = NULL;
+ result = gzclose(fp->u.gzfp);
+ fp->u.gzfp = NULL;
+ return result;
}
- else
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t res, cnt;
- if (fp->zstd.cstream)
+ if (fp->u.zstd.cstream)
{
for (;;)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_end);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
if (res == 0)
break;
}
- ZSTD_freeCStream(fp->zstd.cstream);
- pg_free(fp->zstd.output.dst);
+ ZSTD_freeCStream(fp->u.zstd.cstream);
+ pg_free(fp->u.zstd.output.dst);
}
- if (fp->zstd.dstream)
+ if (fp->u.zstd.dstream)
{
- ZSTD_freeDStream(fp->zstd.dstream);
- pg_free(unconstify(void *, fp->zstd.input.src));
+ ZSTD_freeDStream(fp->u.zstd.dstream);
+ pg_free(unconstify(void *, fp->u.zstd.input.src));
}
- result = fclose(fp->zstd.fp);
- fp->zstd.fp = NULL;
+ result = fclose(fp->u.zstd.fp);
+ fp->u.zstd.fp = NULL;
+ return result;
}
- else
#endif
- {
- result = fclose(fp->uncompressedfp);
- fp->uncompressedfp = NULL;
- }
- free_keep_errno(fp);
+ result = fclose(fp->u.fp);
+ fp->u.fp = NULL;
+ free_keep_errno(fp);
return result;
}
@@ -1057,26 +1050,26 @@ int
cfeof(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzeof(fp->compressedfp);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzeof(fp->u.gzfp);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
- return feof(fp->zstd.fp);
- else
+ if (fp->alg == COMPR_ALG_ZSTD)
+ return feof(fp->u.zstd.fp);
#endif
- return feof(fp->uncompressedfp);
+
+ return feof(fp->u.fp);
}
const char *
get_cfp_error(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
if (errnum != Z_ERRNO)
return errmsg;
--
2.17.0
0006-Move-zlib-into-the-union.patchtext/x-diff; charset=us-asciiDownload
From d52903ad3661cf62c375371515a702a60beb3f63 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 11 Dec 2020 22:22:31 -0600
Subject: [PATCH 6/7] Move zlib into the union{}
---
src/bin/pg_dump/compress_io.c | 54 ++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 26 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index ae1ae7b0d0..d93bc61fd8 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -67,23 +67,25 @@ struct CompressorState
CompressionAlgorithm comprAlg;
WriteFunc writeF;
+ union {
#ifdef HAVE_LIBZ
- z_streamp zp;
- char *zlibOut;
- size_t zlibOutSize;
+ struct {
+ z_streamp zp;
+ char *zlibOut;
+ size_t zlibOutSize;
+ } zlib;
#endif
#ifdef HAVE_LIBZSTD
- union {
+ /* This is used for compression but not decompression */
struct {
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
// XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
+ ZSTD_CStream *cstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
} zstd;
- } u;
#endif
-
+ } u;
};
/* Routines that support zlib compressed data I/O */
@@ -415,7 +417,7 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
- zp = cs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
+ zp = cs->u.zlib.zp = (z_streamp) pg_malloc(sizeof(z_stream));
zp->zalloc = Z_NULL;
zp->zfree = Z_NULL;
zp->opaque = Z_NULL;
@@ -425,22 +427,22 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
* actually allocate one extra byte because some routines want to append a
* trailing zero byte to the zlib output.
*/
- cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
- cs->zlibOutSize = ZLIB_OUT_SIZE;
+ cs->u.zlib.zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
+ cs->u.zlib.zlibOutSize = ZLIB_OUT_SIZE;
if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
/* Just be paranoid - maybe End is called after Start, with no Write */
- zp->next_out = (void *) cs->zlibOut;
- zp->avail_out = cs->zlibOutSize;
+ zp->next_out = (void *) cs->u.zlib.zlibOut;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
static void
EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
{
- z_streamp zp = cs->zp;
+ z_streamp zp = cs->u.zlib.zp;
zp->next_in = NULL;
zp->avail_in = 0;
@@ -451,23 +453,23 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
if (deflateEnd(zp) != Z_OK)
fatal("could not close compression stream: %s", zp->msg);
- free(cs->zlibOut);
- free(cs->zp);
+ free(cs->u.zlib.zlibOut);
+ free(cs->u.zlib.zp);
}
static void
DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
{
- z_streamp zp = cs->zp;
- char *out = cs->zlibOut;
+ z_streamp zp = cs->u.zlib.zp;
+ char *out = cs->u.zlib.zlibOut;
int res = Z_OK;
- while (cs->zp->avail_in != 0 || flush)
+ while (cs->u.zlib.zp->avail_in != 0 || flush)
{
res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
if (res == Z_STREAM_ERROR)
fatal("could not compress data: %s", zp->msg);
- if ((flush && (zp->avail_out < cs->zlibOutSize))
+ if ((flush && (zp->avail_out < cs->u.zlib.zlibOutSize))
|| (zp->avail_out == 0)
|| (zp->avail_in != 0)
)
@@ -477,18 +479,18 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
* chunk is the EOF marker in the custom format. This should never
* happen but...
*/
- if (zp->avail_out < cs->zlibOutSize)
+ if (zp->avail_out < cs->u.zlib.zlibOutSize)
{
/*
* Any write function should do its own error checking but to
* make sure we do a check here as well...
*/
- size_t len = cs->zlibOutSize - zp->avail_out;
+ size_t len = cs->u.zlib.zlibOutSize - zp->avail_out;
cs->writeF(AH, out, len);
}
zp->next_out = (void *) out;
- zp->avail_out = cs->zlibOutSize;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
if (res == Z_STREAM_END)
@@ -500,8 +502,8 @@ static void
WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen)
{
- cs->zp->next_in = (void *) unconstify(char *, data);
- cs->zp->avail_in = dLen;
+ cs->u.zlib.zp->next_in = (void *) unconstify(char *, data);
+ cs->u.zlib.zp->avail_in = dLen;
DeflateCompressorZlib(AH, cs, false);
}
--
2.17.0
Justin Pryzby <pryzby@telsasoft.com> writes:
I found that our largest tables are 40% smaller and 20% faster to pipe
pg_dump -Fc -Z0 |zstd relative to native zlib
The patch might be a tad smaller if you hadn't included a core file in it.
regards, tom lane
On Mon, Dec 21, 2020 at 03:02:40PM -0500, Tom Lane wrote:
Justin Pryzby <pryzby@telsasoft.com> writes:
I found that our largest tables are 40% smaller and 20% faster to pipe
pg_dump -Fc -Z0 |zstd relative to native zlibThe patch might be a tad smaller if you hadn't included a core file in it.
About 89% smaller.
This also fixes the extension (.zst)
And fixes zlib default compression.
And a bunch of cleanup.
--
Justin
Attachments:
0001-fix-pre-existing-docs-comments.patchtext/x-diff; charset=us-asciiDownload
From c506c8a93ae70726a5b4b33a1d0098caf5665f3a Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 21 Dec 2020 00:32:32 -0600
Subject: [PATCH 1/7] fix pre-existing docs/comments
---
doc/src/sgml/ref/pg_dump.sgml | 2 +-
src/bin/pg_dump/pg_backup_directory.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 0aa35cf0c3..dcb25dc3cd 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -621,7 +621,7 @@ PostgreSQL documentation
<listitem>
<para>
Specify the compression level to use. Zero means no compression.
- For the custom archive format, this specifies compression of
+ For the custom and directory archive formats, this specifies compression of
individual table-data segments, and the default is to compress
at a moderate level.
For plain text output, setting a nonzero compression level causes
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 48fa7cb1a3..650b542fce 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -4,7 +4,7 @@
*
* A directory format dump is a directory, which contains a "toc.dat" file
* for the TOC, and a separate file for each data entry, named "<oid>.dat".
- * Large objects (BLOBs) are stored in separate files named "blob_<uid>.dat",
+ * Large objects (BLOBs) are stored in separate files named "blob_<oid>.dat",
* and there's a plain-text TOC file for them called "blobs.toc". If
* compression is used, each data file is individually compressed and the
* ".gz" suffix is added to the filenames. The TOC files are never
--
2.17.0
0002-Fix-malformed-comment.patchtext/x-diff; charset=us-asciiDownload
From 49f400b5b2e559cc2ed6be82e04a4dc4ba5c63b3 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat, 12 Dec 2020 00:11:37 -0600
Subject: [PATCH 2/7] Fix malformed comment..
broken since bf50caf10.
---
src/bin/pg_dump/pg_backup_archiver.h | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 177360ed6e..6a5a22637b 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -329,9 +329,12 @@ struct _archiveHandle
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
- int compression; /* Compression requested on open Possible
- * values for compression: -1
- * Z_DEFAULT_COMPRESSION 0 COMPRESSION_NONE
+ int compression; /*---------
+ * Compression requested on open
+ * Possible values for compression:
+ * -2 ZSTD_COMPRESSION
+ * -1 Z_DEFAULT_COMPRESSION
+ * 0 COMPRESSION_NONE
* 1-9 levels for gzip compression */
bool dosync; /* data requested to be synced on sight */
ArchiveMode mode; /* File mode - r or w */
--
2.17.0
0003-pg_dump-zstd-compression.patchtext/x-diff; charset=us-asciiDownload
From 8a8939a3060c984af289b5fe62754d94f2675248 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 11 Dec 2020 15:01:25 -0600
Subject: [PATCH 3/7] pg_dump: zstd compression
document any change in search for .gz?
docs
---
configure | 123 ++++++-
configure.ac | 22 ++
src/bin/pg_dump/compress_io.c | 475 ++++++++++++++++++++++++--
src/bin/pg_dump/compress_io.h | 5 +-
src/bin/pg_dump/pg_backup_archiver.h | 5 +
src/bin/pg_dump/pg_backup_directory.c | 6 +-
src/bin/pg_dump/pg_dump.c | 3 +-
src/include/pg_config.h.in | 3 +
8 files changed, 597 insertions(+), 45 deletions(-)
diff --git a/configure b/configure
index 11a4284e5b..240e536e04 100755
--- a/configure
+++ b/configure
@@ -698,6 +698,7 @@ with_gnu_ld
LD
LDFLAGS_SL
LDFLAGS_EX
+with_zstd
with_zlib
with_system_tzdata
with_libxslt
@@ -798,6 +799,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -866,6 +868,7 @@ with_libxml
with_libxslt
with_system_tzdata
with_zlib
+with_zstd
with_gnu_ld
enable_largefile
'
@@ -935,6 +938,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1187,6 +1191,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1324,7 +1337,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1477,6 +1490,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1570,6 +1584,7 @@ Optional Packages:
--with-system-tzdata=DIR
use system time zone data in DIR
--without-zlib do not use Zlib
+ --without-zstd do not use Zstd
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
Some influential environment variables:
@@ -8601,6 +8616,35 @@ fi
+#
+# Zstd
+#
+
+
+
+# Check whether --with-zstd was given.
+if test "${with_zstd+set}" = set; then :
+ withval=$with_zstd;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ as_fn_error $? "no argument expected for --with-zstd option" "$LINENO" 5
+ ;;
+ esac
+
+else
+ with_zstd=yes
+
+fi
+
+
+
+
#
# Assignments
#
@@ -12092,6 +12136,59 @@ fi
fi
+if test "$with_zstd" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZSTD_compressStream2 in -lzstd" >&5
+$as_echo_n "checking for ZSTD_compressStream2 in -lzstd... " >&6; }
+if ${ac_cv_lib_zstd_ZSTD_compressStream2+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzstd $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ZSTD_compressStream2 ();
+int
+main ()
+{
+return ZSTD_compressStream2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_zstd_ZSTD_compressStream2=yes
+else
+ ac_cv_lib_zstd_ZSTD_compressStream2=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_zstd_ZSTD_compressStream2" >&5
+$as_echo "$ac_cv_lib_zstd_ZSTD_compressStream2" >&6; }
+if test "x$ac_cv_lib_zstd_ZSTD_compressStream2" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZSTD 1
+_ACEOF
+
+ LIBS="-lzstd $LIBS"
+
+else
+ as_fn_error $? "zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+fi
+
if test "$enable_spinlocks" = yes; then
$as_echo "#define HAVE_SPINLOCKS 1" >>confdefs.h
@@ -13295,6 +13392,20 @@ Use --without-zlib to disable zlib support." "$LINENO" 5
fi
+fi
+
+if test "$with_zstd" = yes; then
+ ac_fn_c_check_header_mongrel "$LINENO" "zstd.h" "ac_cv_header_zstd_h" "$ac_includes_default"
+if test "x$ac_cv_header_zstd_h" = xyes; then :
+
+else
+ as_fn_error $? "zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+
fi
if test "$with_gssapi" = yes ; then
@@ -14689,7 +14800,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14735,7 +14846,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14759,7 +14870,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14804,7 +14915,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14828,7 +14939,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
diff --git a/configure.ac b/configure.ac
index fc523c6aeb..7f7222159a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -999,6 +999,13 @@ PGAC_ARG_BOOL(with, zlib, yes,
[do not use Zlib])
AC_SUBST(with_zlib)
+#
+# Zstd
+#
+PGAC_ARG_BOOL(with, zstd, yes,
+ [do not use Zstd])
+AC_SUBST(with_zstd)
+
#
# Assignments
#
@@ -1186,6 +1193,14 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_LIB(zstd, ZSTD_compressStream2, [],
+ [AC_MSG_ERROR([zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$enable_spinlocks" = yes; then
AC_DEFINE(HAVE_SPINLOCKS, 1, [Define to 1 if you have spinlocks.])
else
@@ -1400,6 +1415,13 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_HEADER(zstd.h, [], [AC_MSG_ERROR([zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$with_gssapi" = yes ; then
AC_CHECK_HEADERS(gssapi/gssapi.h, [],
[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 1417401086..b51ba680a2 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -13,7 +13,7 @@
* friends, providing an interface similar to those, but abstracts away
* the possible compression. Both APIs use libz for the compression, but
* the second API uses gzip headers, so the resulting files can be easily
- * manipulated with the gzip utility.
+ * manipulated with the gzip utility. XXX
*
* Compressor API
* --------------
@@ -41,7 +41,7 @@
* libz's gzopen() APIs. It allows you to use the same functions for
* compressed and uncompressed streams. cfopen_read() first tries to open
* the file with given name, and if it fails, it tries to open the same
- * file with the .gz suffix. cfopen_write() opens a file for writing, an
+ * file with the .gz suffix. cfopen_write() opens a file for writing, an XXX
* extra argument specifies if the file should be compressed, and adds the
* .gz suffix to the filename if so. This allows you to easily handle both
* compressed and uncompressed files.
@@ -72,6 +72,18 @@ struct CompressorState
char *zlibOut;
size_t zlibOutSize;
#endif
+
+#ifdef HAVE_LIBZSTD
+ union {
+ struct {
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ } zstd;
+ } u;
+#endif
+
};
static void ParseCompressionOption(int compression, CompressionAlgorithm *alg,
@@ -88,6 +100,15 @@ static void WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
static void EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs);
#endif
+#ifdef HAVE_LIBZSTD
+static void InitCompressorZstd(CompressorState *cs, int level);
+static void EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
+static void DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
+static void WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen);
+static void ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF);
+#endif
+
/* Routines that support uncompressed data I/O */
static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
@@ -101,15 +122,25 @@ static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
static void
ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
{
- if (compression == Z_DEFAULT_COMPRESSION ||
- (compression > 0 && compression <= 9))
- *alg = COMPR_ALG_LIBZ;
- else if (compression == 0)
- *alg = COMPR_ALG_NONE;
- else
+ switch (compression)
{
- fatal("invalid compression code: %d", compression);
- *alg = COMPR_ALG_NONE; /* keep compiler quiet */
+#ifdef HAVE_LIBZSTD
+ case ZSTD_COMPRESSION:
+ *alg = COMPR_ALG_ZSTD;
+ break;
+#endif
+#ifdef HAVE_ZLIB
+ case Z_DEFAULT_COMPRESSION:
+ case 1..9:
+ *alg = COMPR_ALG_LIBZ;
+ break;
+#endif
+ case 0:
+ *alg = COMPR_ALG_NONE;
+ break;
+ default:
+ fatal("invalid compression code: %d", compression);
+ *alg = COMPR_ALG_NONE; /* keep compiler quiet */
}
/* The level is just the passed-in value. */
@@ -141,10 +172,23 @@ AllocateCompressor(int compression, WriteFunc writeF)
/*
* Perform compression algorithm specific initialization.
*/
+ switch (alg)
+ {
#ifdef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
+ case COMPR_ALG_LIBZ:
InitCompressorZlib(cs, level);
+ break;
+#endif
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ InitCompressorZstd(cs, level);
+ break;
#endif
+ case COMPR_ALG_NONE:
+ /* Do nothing */
+ break;
+ // default:
+ }
return cs;
}
@@ -162,12 +206,20 @@ ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
if (alg == COMPR_ALG_NONE)
ReadDataFromArchiveNone(AH, readF);
- if (alg == COMPR_ALG_LIBZ)
+ else if (alg == COMPR_ALG_LIBZ)
{
#ifdef HAVE_LIBZ
ReadDataFromArchiveZlib(AH, readF);
#else
fatal("not built with zlib support");
+#endif
+ }
+ else if (alg == COMPR_ALG_ZSTD)
+ {
+#ifdef HAVE_LIBZSTD
+ ReadDataFromArchiveZstd(AH, readF);
+#else
+ fatal("not built with zstd support");
#endif
}
}
@@ -188,6 +240,15 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
fatal("not built with zlib support");
#endif
break;
+
+ case COMPR_ALG_ZSTD:
+#ifdef HAVE_LIBZSTD
+ WriteDataToArchiveZstd(AH, cs, data, dLen);
+#else
+ fatal("not built with zstd support");
+#endif
+ break;
+
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
@@ -204,12 +265,172 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
if (cs->comprAlg == COMPR_ALG_LIBZ)
EndCompressorZlib(AH, cs);
#endif
+#ifdef HAVE_LIBZSTD
+ if (cs->comprAlg == COMPR_ALG_ZSTD)
+ EndCompressorZstd(AH, cs);
+#endif
+
free(cs);
}
/* Private routines, specific to each compression method. */
+// XXX: put in separate files ?
+
+#ifdef HAVE_LIBZSTD
+static void
+InitCompressorZstd(CompressorState *cs, int level)
+{
+ cs->u.zstd.cstream = ZSTD_createCStream();
+ if (cs->u.zstd.cstream == NULL)
+ fatal("could not initialize compression library");
+
+ /* XXX: initialize safely like the corresponding zlib "paranoia" */
+ cs->u.zstd.output.size = ZSTD_CStreamOutSize();
+ cs->u.zstd.output.dst = pg_malloc(cs->u.zstd.output.size);
+ cs->u.zstd.output.pos = 0;
+}
+
+static void
+EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+{
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ for (;;)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ &cs->u.zstd.input, ZSTD_e_end);
+
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ if (res == 0)
+ break;
+
+ if (ZSTD_isError(res))
+ fatal("could not close compression stream: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ // XXX: retval
+ ZSTD_freeCStream(cs->u.zstd.cstream);
+}
+
+static void
+DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+{
+ ZSTD_inBuffer *input = &cs->u.zstd.input;
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ while (input->pos != input->size)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ input, ZSTD_e_continue);
+
+ if (output->pos == output->size ||
+ input->pos != input->size)
+ {
+ /*
+ * Extra paranoia: avoid zero-length chunks, since a zero length
+ * chunk is the EOF marker in the custom format. This should never
+ * happen but...
+ */
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ output->pos = 0;
+ }
+
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ }
+}
+
+static void
+WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen)
+{
+ cs->u.zstd.input.src = (void *) unconstify(char *, data);
+ cs->u.zstd.input.size = dLen;
+ cs->u.zstd.input.pos = 0;
+ DeflateCompressorZstd(AH, cs);
+}
+
+/* Read data from a compressed zstd archive */
+static void
+ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
+{
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ size_t res;
+ size_t input_size;
+
+ dstream = ZSTD_createDStream();
+ if (dstream == NULL)
+ fatal("could not initialize compression library");
+
+ input_size = ZSTD_DStreamInSize();
+ input.src = pg_malloc(input_size);
+
+ output.size = ZSTD_DStreamOutSize();
+ output.dst = pg_malloc(output.size);
+
+ /* read compressed data */
+ for (;;)
+ {
+ size_t cnt;
+
+ input.size = input_size; // XXX: the buffer can grow, we shouldn't keep resetting it to the original value..
+ cnt = readF(AH, (char **)unconstify(void **, &input.src), &input.size);
+ input.pos = 0;
+ input.size = cnt;
+
+ if (cnt == 0)
+ break;
+
+ while (input.pos < input.size)
+ {
+ /* decompress */
+ output.pos = 0;
+ res = ZSTD_decompressStream(dstream, &output, &input);
+
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+
+ /* write to output handle */
+ ((char *)output.dst)[output.pos] = '\0';
+ ahwrite(output.dst, 1, output.pos, AH);
+ }
+ }
+
+ /* write any remainder to output handle */
+ /* XXX: is it needed? */
+ /* If `input.pos < input.size`, some input has not been consumed. */
+ /* But if `output.pos == output.size`, there might be some data left within internal buffers., */
+#if 0
+ while (input.pos < input.size || output.pos == output.size)
+ {
+ output.pos = 0;
+ res = ZSTD_decompressStream(dstream, &output, &input);
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+ ((char *)output.dst)[output.pos] = '\0';
+ ahwrite(output.dst, 1, output.pos, AH);
+ }
+#endif
+
+ pg_free(unconstify(void *, input.src));
+ pg_free(output.dst);
+}
+
+#endif /* HAVE_LIBZSTD */
#ifdef HAVE_LIBZ
+
/*
* Functions for zlib compressed output.
*/
@@ -422,6 +643,19 @@ struct cfp
#ifdef HAVE_LIBZ
gzFile compressedfp;
#endif
+
+#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
+ /* This is a normal file to which we read/write compressed data */
+ struct {
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
+#endif
+
};
#ifdef HAVE_LIBZ
@@ -449,24 +683,25 @@ free_keep_errno(void *p)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_read(const char *path, const char *mode)
+cfopen_read(const char *path, const char *mode, int compression)
{
cfp *fp;
#ifdef HAVE_LIBZ
- if (hasSuffix(path, ".gz"))
- fp = cfopen(path, mode, 1);
+ if (hasSuffix(path, ".gz") || hasSuffix(path, ".zst"))
+ fp = cfopen(path, mode, compression);
else
#endif
{
- fp = cfopen(path, mode, 0);
+ fp = cfopen(path, mode, compression);
#ifdef HAVE_LIBZ
if (fp == NULL)
{
char *fname;
+ char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
- fname = psprintf("%s.gz", path);
- fp = cfopen(fname, mode, 1);
+ fname = psprintf("%s.%s", path, suffix);
+ fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
}
#endif
@@ -491,13 +726,14 @@ cfopen_write(const char *path, const char *mode, int compression)
cfp *fp;
if (compression == 0)
- fp = cfopen(path, mode, 0);
+ fp = cfopen(path, mode, compression);
else
{
-#ifdef HAVE_LIBZ
+#ifdef HAVE_LIBZ // XXX
char *fname;
+ char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
- fname = psprintf("%s.gz", path);
+ fname = psprintf("%s.%s", path, suffix);
fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
#else
@@ -505,11 +741,12 @@ cfopen_write(const char *path, const char *mode, int compression)
fp = NULL; /* keep compiler quiet */
#endif
}
+
return fp;
}
/*
- * Opens file 'path' in 'mode'. If 'compression' is non-zero, the file
+ * Opens file 'path' in 'mode'. If 'alg' is COMPR_ALG_ZLIB, the file
* is opened with libz gzopen(), otherwise with plain fopen().
*
* On failure, return NULL with an error code in errno.
@@ -519,9 +756,19 @@ cfopen(const char *path, const char *mode, int compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
- if (compression != 0)
+ fp->uncompressedfp = NULL;
+#ifdef HAVE_LIBZ
+ fp->compressedfp = NULL;
+#endif
+#ifdef HAVE_LIBZSTD
+ fp->zstd.fp = NULL;
+#endif
+
+ switch (compression)
{
#ifdef HAVE_LIBZ
+ case 1 ... 9: // XXX: nonportable
+ case Z_DEFAULT_COMPRESSION:
if (compression != Z_DEFAULT_COMPRESSION)
{
/* user has specified a compression level, so tell zlib to use it */
@@ -537,30 +784,57 @@ cfopen(const char *path, const char *mode, int compression)
fp->compressedfp = gzopen(path, mode);
}
- fp->uncompressedfp = NULL;
if (fp->compressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
-#else
- fatal("not built with zlib support");
+
+ return fp;
+ break;
#endif
- }
- else
- {
-#ifdef HAVE_LIBZ
- fp->compressedfp = NULL;
+
+#ifdef HAVE_LIBZSTD
+ case ZSTD_COMPRESSION:
+ fp->zstd.fp = fopen(path, mode);
+ // XXX: save the compression params
+ if (fp->zstd.fp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ else if (strchr(mode, 'w'))
+ {
+ fp->zstd.dstream = NULL;
+ fp->zstd.output.size = ZSTD_CStreamOutSize(); // XXX
+ fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
+ fp->zstd.cstream = ZSTD_createCStream();
+ if (fp->zstd.cstream == NULL)
+ fatal("could not initialize compression library");
+ }
+ else if (strchr(mode, 'r'))
+ {
+ fp->zstd.cstream = NULL;
+ fp->zstd.input.size = ZSTD_DStreamOutSize(); // XXX
+ fp->zstd.input.src = pg_malloc0(fp->zstd.input.size);
+ fp->zstd.dstream = ZSTD_createDStream();
+ if (fp->zstd.dstream == NULL)
+ fatal("could not initialize compression library");
+ } // XXX else: bad mode
+ return fp;
+ break;
#endif
+
+ default:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
- }
- return fp;
+ return fp;
+ }
}
@@ -587,6 +861,44 @@ cfread(void *ptr, int size, cfp *fp)
}
else
#endif
+
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t input_size = ZSTD_DStreamInSize();
+ size_t res, cnt;
+
+ output->size = size;
+ output->dst = ptr;
+ output->pos = 0;
+
+ /* read compressed data */
+ while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp)))
+ {
+ input->size = cnt;
+ input->pos = 0;
+
+ for ( ; input->pos < input->size; )
+ {
+ /* decompress */
+ res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ if (res == 0 || output->pos == output->size)
+ break;
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+ }
+
+ if (output->pos == output->size)
+ break; /* We read all the data that fits */
+ }
+
+ ret = output->pos;
+ }
+ else
+#endif
+
{
ret = fread(ptr, 1, size, fp->uncompressedfp);
if (ret != size && !feof(fp->uncompressedfp))
@@ -603,6 +915,35 @@ cfwrite(const void *ptr, int size, cfp *fp)
return gzwrite(fp->compressedfp, ptr, size);
else
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ size_t res, cnt;
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+
+ input->src = ptr;
+ input->size = size;
+ input->pos = 0;
+
+ /* Consume all input, and flush later */
+ while (input->pos != input->size)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ }
+
+ return size;
+ }
+ else
+#endif
+
return fwrite(ptr, 1, size, fp->uncompressedfp);
}
@@ -625,6 +966,20 @@ cfgetc(cfp *fp)
}
else
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ if (cfread(&ret, 1, fp) != 1)
+ {
+ if (feof(fp->zstd.fp))
+ fatal("could not read from input file: end of file");
+ else
+ fatal("could not read from input file: %s", strerror(errno));
+ }
+fprintf(stderr, "cfgetc %d\n", ret);
+ }
+#endif
+
{
ret = fgetc(fp->uncompressedfp);
if (ret == EOF)
@@ -641,6 +996,18 @@ cfgets(cfp *fp, char *buf, int len)
if (fp->compressedfp)
return gzgets(fp->compressedfp, buf, len);
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ int res;
+ res = cfread(buf, len, fp);
+ buf[res] = 0;
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = '\0';
+ return buf;
+ }
+ else
#endif
return fgets(buf, len, fp->uncompressedfp);
}
@@ -662,6 +1029,43 @@ cfclose(cfp *fp)
fp->compressedfp = NULL;
}
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t res, cnt;
+
+ if (fp->zstd.cstream)
+ {
+ for (;;)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ if (res == 0)
+ break;
+ }
+
+ ZSTD_freeCStream(fp->zstd.cstream);
+ pg_free(fp->zstd.output.dst);
+ }
+
+ if (fp->zstd.dstream)
+ {
+ ZSTD_freeDStream(fp->zstd.dstream);
+ pg_free(unconstify(void *, fp->zstd.input.src));
+ }
+
+ result = fclose(fp->zstd.fp);
+ fp->zstd.fp = NULL;
+ }
+ else
#endif
{
result = fclose(fp->uncompressedfp);
@@ -679,6 +1083,11 @@ cfeof(cfp *fp)
if (fp->compressedfp)
return gzeof(fp->compressedfp);
else
+#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ return feof(fp->zstd.fp);
+ else
#endif
return feof(fp->uncompressedfp);
}
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index d2e6e1b854..f0ce06f176 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -24,7 +24,8 @@
typedef enum
{
COMPR_ALG_NONE,
- COMPR_ALG_LIBZ
+ COMPR_ALG_LIBZ,
+ COMPR_ALG_ZSTD,
} CompressionAlgorithm;
/* Prototype for callback function to WriteDataToArchive() */
@@ -57,7 +58,7 @@ extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
typedef struct cfp cfp;
extern cfp *cfopen(const char *path, const char *mode, int compression);
-extern cfp *cfopen_read(const char *path, const char *mode);
+extern cfp *cfopen_read(const char *path, const char *mode, int compression);
extern cfp *cfopen_write(const char *path, const char *mode, int compression);
extern int cfread(void *ptr, int size, cfp *fp);
extern int cfwrite(const void *ptr, int size, cfp *fp);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 6a5a22637b..1a229ebedb 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -60,6 +60,11 @@ typedef struct _z_stream
typedef z_stream *z_streamp;
#endif
+#ifdef HAVE_LIBZSTD
+#include <zstd.h>
+#define ZSTD_COMPRESSION -2
+#endif /* HAVE_LIBZSTD */
+
/* Data block types */
#define BLK_DATA 1
#define BLK_BLOBS 3
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 650b542fce..6e55cb425e 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -202,7 +202,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
setFilePath(AH, fname, "toc.dat");
- tocFH = cfopen_read(fname, PG_BINARY_R);
+ tocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
if (tocFH == NULL)
fatal("could not open input file \"%s\": %m", fname);
@@ -388,7 +388,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!filename)
return;
- cfp = cfopen_read(filename, PG_BINARY_R);
+ cfp = cfopen_read(filename, PG_BINARY_R, AH->compression);
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
@@ -440,7 +440,7 @@ _LoadBlobs(ArchiveHandle *AH)
setFilePath(AH, fname, "blobs.toc");
- ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R);
+ ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
if (ctx->blobsTocFH == NULL)
fatal("could not open large object TOC file \"%s\" for input: %m",
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8b1e5cc2b5..f21eb021c7 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -533,7 +533,8 @@ main(int argc, char **argv)
case 'Z': /* Compression Level */
compressLevel = atoi(optarg);
- if (compressLevel < 0 || compressLevel > 9)
+ if ((compressLevel < 0 || compressLevel > 9) &&
+ compressLevel != -2)
{
pg_log_error("compression level must be in range 0..9");
exit_nicely(1);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index de8f838e53..da35415c72 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -346,6 +346,9 @@
/* Define to 1 if you have the `z' library (-lz). */
#undef HAVE_LIBZ
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+#undef HAVE_LIBZSTD
+
/* Define to 1 if you have the `link' function. */
#undef HAVE_LINK
--
2.17.0
0004-cmdline-parser-for-compression-alg-level-opts.patchtext/x-diff; charset=us-asciiDownload
From 4b87bdba077da1e1609a1d7dbb40772d432c28dd Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sat, 12 Dec 2020 00:40:39 -0600
Subject: [PATCH 4/7] cmdline parser for compression alg/level/opts
---
src/bin/pg_dump/compress_io.c | 235 ++++++++++++--------------
src/bin/pg_dump/compress_io.h | 20 +--
src/bin/pg_dump/pg_backup.h | 19 ++-
src/bin/pg_dump/pg_backup_archiver.c | 45 ++---
src/bin/pg_dump/pg_backup_archiver.h | 11 +-
src/bin/pg_dump/pg_backup_custom.c | 6 +-
src/bin/pg_dump/pg_backup_directory.c | 19 ++-
src/bin/pg_dump/pg_backup_tar.c | 33 ++--
src/bin/pg_dump/pg_dump.c | 157 ++++++++++++++---
9 files changed, 324 insertions(+), 221 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index b51ba680a2..421b8e04cb 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -86,12 +86,9 @@ struct CompressorState
};
-static void ParseCompressionOption(int compression, CompressionAlgorithm *alg,
- int *level);
-
/* Routines that support zlib compressed data I/O */
#ifdef HAVE_LIBZ
-static void InitCompressorZlib(CompressorState *cs, int level);
+static void InitCompressorZlib(CompressorState *cs, Compress *compress);
static void DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs,
bool flush);
static void ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF);
@@ -101,9 +98,9 @@ static void EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs);
#endif
#ifdef HAVE_LIBZSTD
-static void InitCompressorZstd(CompressorState *cs, int level);
+static ZSTD_CStream *ZstdCStreamParams(Compress *compress);
+static void InitCompressorZstd(CompressorState *cs, Compress *compress);
static void EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
-static void DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
static void WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen);
static void ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF);
@@ -114,80 +111,40 @@ static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen);
-/*
- * Interprets a numeric 'compression' value. The algorithm implied by the
- * value (zlib or none at the moment), is returned in *alg, and the
- * zlib compression level in *level.
- */
-static void
-ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
-{
- switch (compression)
- {
-#ifdef HAVE_LIBZSTD
- case ZSTD_COMPRESSION:
- *alg = COMPR_ALG_ZSTD;
- break;
-#endif
-#ifdef HAVE_ZLIB
- case Z_DEFAULT_COMPRESSION:
- case 1..9:
- *alg = COMPR_ALG_LIBZ;
- break;
-#endif
- case 0:
- *alg = COMPR_ALG_NONE;
- break;
- default:
- fatal("invalid compression code: %d", compression);
- *alg = COMPR_ALG_NONE; /* keep compiler quiet */
- }
-
- /* The level is just the passed-in value. */
- if (level)
- *level = compression;
-}
-
/* Public interface routines */
/* Allocate a new compressor */
CompressorState *
-AllocateCompressor(int compression, WriteFunc writeF)
+AllocateCompressor(Compress *compression, WriteFunc writeF)
{
CompressorState *cs;
- CompressionAlgorithm alg;
- int level;
-
- ParseCompressionOption(compression, &alg, &level);
-
-#ifndef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
- fatal("not built with zlib support");
-#endif
cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
cs->writeF = writeF;
- cs->comprAlg = alg;
+ cs->comprAlg = compression->alg;
/*
* Perform compression algorithm specific initialization.
*/
- switch (alg)
+ Assert (compression->alg != COMPR_ALG_DEFAULT);
+ switch (compression->alg)
{
#ifdef HAVE_LIBZ
case COMPR_ALG_LIBZ:
- InitCompressorZlib(cs, level);
+ InitCompressorZlib(cs, compression);
break;
#endif
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- InitCompressorZstd(cs, level);
+ InitCompressorZstd(cs, compression);
break;
#endif
case COMPR_ALG_NONE:
/* Do nothing */
break;
- // default:
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
return cs;
@@ -198,12 +155,9 @@ AllocateCompressor(int compression, WriteFunc writeF)
* out with ahwrite().
*/
void
-ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
+ReadDataFromArchive(ArchiveHandle *AH, ReadFunc readF)
{
- CompressionAlgorithm alg;
-
- ParseCompressionOption(compression, &alg, NULL);
-
+ CompressionAlgorithm alg = AH->compression.alg;
if (alg == COMPR_ALG_NONE)
ReadDataFromArchiveNone(AH, readF);
else if (alg == COMPR_ALG_LIBZ)
@@ -233,25 +187,25 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
{
switch (cs->comprAlg)
{
- case COMPR_ALG_LIBZ:
#ifdef HAVE_LIBZ
+ case COMPR_ALG_LIBZ:
WriteDataToArchiveZlib(AH, cs, data, dLen);
-#else
- fatal("not built with zlib support");
-#endif
break;
+#endif
- case COMPR_ALG_ZSTD:
#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
WriteDataToArchiveZstd(AH, cs, data, dLen);
-#else
- fatal("not built with zstd support");
-#endif
break;
+#endif
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
+
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -277,13 +231,42 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
// XXX: put in separate files ?
#ifdef HAVE_LIBZSTD
-static void
-InitCompressorZstd(CompressorState *cs, int level)
+static ZSTD_CStream*
+ZstdCStreamParams(Compress *compress)
{
- cs->u.zstd.cstream = ZSTD_createCStream();
- if (cs->u.zstd.cstream == NULL)
+ size_t res;
+ ZSTD_CStream *cstream;
+
+ cstream = ZSTD_createCStream();
+ if (cstream == NULL)
fatal("could not initialize compression library");
+ if (compress->level != 0) // XXX: ZSTD_CLEVEL_DEFAULT
+ {
+ res = ZSTD_CCtx_setParameter(cstream,
+ ZSTD_c_compressionLevel, compress->level);
+ if (ZSTD_isError(res))
+ fatal("could not set compression level: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ if (compress->zstdlong)
+ {
+ res = ZSTD_CCtx_setParameter(cstream,
+ ZSTD_c_enableLongDistanceMatching, 1); // XXX: allow to disable it
+ if (ZSTD_isError(res))
+ fatal("could not set compression parameter: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ return cstream;
+}
+
+static void
+InitCompressorZstd(CompressorState *cs, Compress *compress)
+{
+ cs->u.zstd.cstream = ZstdCStreamParams(compress);
+
/* XXX: initialize safely like the corresponding zlib "paranoia" */
cs->u.zstd.output.size = ZSTD_CStreamOutSize();
cs->u.zstd.output.dst = pg_malloc(cs->u.zstd.output.size);
@@ -295,6 +278,8 @@ EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
{
ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+
for (;;)
{
size_t res;
@@ -318,11 +303,16 @@ EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
}
static void
-DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen)
{
ZSTD_inBuffer *input = &cs->u.zstd.input;
ZSTD_outBuffer *output = &cs->u.zstd.output;
+ input->src = (void *) unconstify(char *, data);
+ input->size = dLen;
+ input->pos = 0;
+
while (input->pos != input->size)
{
size_t res;
@@ -349,16 +339,6 @@ DeflateCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
}
}
-static void
-WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
- const char *data, size_t dLen)
-{
- cs->u.zstd.input.src = (void *) unconstify(char *, data);
- cs->u.zstd.input.size = dLen;
- cs->u.zstd.input.pos = 0;
- DeflateCompressorZstd(AH, cs);
-}
-
/* Read data from a compressed zstd archive */
static void
ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
@@ -436,7 +416,7 @@ ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
*/
static void
-InitCompressorZlib(CompressorState *cs, int level)
+InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
@@ -453,7 +433,7 @@ InitCompressorZlib(CompressorState *cs, int level)
cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
cs->zlibOutSize = ZLIB_OUT_SIZE;
- if (deflateInit(zp, level) != Z_OK)
+ if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
@@ -676,35 +656,31 @@ free_keep_errno(void *p)
* Open a file for reading. 'path' is the file to open, and 'mode' should
* be either "r" or "rb".
*
- * If the file at 'path' does not exist, we append the ".gz" suffix (if 'path'
- * doesn't already have it) and try again. So if you pass "foo" as 'path',
+ * If the file at 'path' does not exist, we search with compressed suffix (if 'path'
+ * doesn't already have one) and try again. So if you pass "foo" as 'path',
* this will open either "foo" or "foo.gz".
*
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_read(const char *path, const char *mode, int compression)
+cfopen_read(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
-#ifdef HAVE_LIBZ
if (hasSuffix(path, ".gz") || hasSuffix(path, ".zst"))
fp = cfopen(path, mode, compression);
else
-#endif
{
fp = cfopen(path, mode, compression);
-#ifdef HAVE_LIBZ
if (fp == NULL)
{
char *fname;
- char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
+ const char *suffix = compress_suffix(compression);
- fname = psprintf("%s.%s", path, suffix);
+ fname = psprintf("%s%s", path, suffix);
fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
}
-#endif
}
return fp;
}
@@ -714,32 +690,26 @@ cfopen_read(const char *path, const char *mode, int compression)
* be a filemode as accepted by fopen() and gzopen() that indicates writing
* ("w", "wb", "a", or "ab").
*
- * If 'compression' is non-zero, a gzip compressed stream is opened, and
- * 'compression' indicates the compression level used. The ".gz" suffix
- * is automatically added to 'path' in that case.
+ * Use compression if specified.
+ * The appropriate suffix is automatically added to 'path' in that case.
*
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_write(const char *path, const char *mode, int compression)
+cfopen_write(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
- if (compression == 0)
+ if (compression->alg == COMPR_ALG_NONE)
fp = cfopen(path, mode, compression);
else
{
-#ifdef HAVE_LIBZ // XXX
char *fname;
- char *suffix = compression == ZSTD_COMPRESSION ? "zst" : "gz";
+ const char *suffix = compress_suffix(compression);
- fname = psprintf("%s.%s", path, suffix);
+ fname = psprintf("%s%s", path, suffix);
fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
-#else
- fatal("not built with zlib support");
- fp = NULL; /* keep compiler quiet */
-#endif
}
return fp;
@@ -752,7 +722,7 @@ cfopen_write(const char *path, const char *mode, int compression)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen(const char *path, const char *mode, int compression)
+cfopen(const char *path, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
@@ -764,18 +734,17 @@ cfopen(const char *path, const char *mode, int compression)
fp->zstd.fp = NULL;
#endif
- switch (compression)
+ switch (compression->alg)
{
#ifdef HAVE_LIBZ
- case 1 ... 9: // XXX: nonportable
- case Z_DEFAULT_COMPRESSION:
- if (compression != Z_DEFAULT_COMPRESSION)
+ case COMPR_ALG_LIBZ:
+ if (compression->level != Z_DEFAULT_COMPRESSION)
{
/* user has specified a compression level, so tell zlib to use it */
char mode_compression[32];
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
- mode, compression);
+ mode, compression->level);
fp->compressedfp = gzopen(path, mode_compression);
}
else
@@ -795,9 +764,8 @@ cfopen(const char *path, const char *mode, int compression)
#endif
#ifdef HAVE_LIBZSTD
- case ZSTD_COMPRESSION:
+ case COMPR_ALG_ZSTD:
fp->zstd.fp = fopen(path, mode);
- // XXX: save the compression params
if (fp->zstd.fp == NULL)
{
free_keep_errno(fp);
@@ -806,16 +774,14 @@ cfopen(const char *path, const char *mode, int compression)
else if (strchr(mode, 'w'))
{
fp->zstd.dstream = NULL;
- fp->zstd.output.size = ZSTD_CStreamOutSize(); // XXX
+ fp->zstd.output.size = ZSTD_CStreamOutSize();
fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
- fp->zstd.cstream = ZSTD_createCStream();
- if (fp->zstd.cstream == NULL)
- fatal("could not initialize compression library");
+ fp->zstd.cstream = ZstdCStreamParams(compression);
}
else if (strchr(mode, 'r'))
{
fp->zstd.cstream = NULL;
- fp->zstd.input.size = ZSTD_DStreamOutSize(); // XXX
+ fp->zstd.input.size = ZSTD_DStreamInSize();
fp->zstd.input.src = pg_malloc0(fp->zstd.input.size);
fp->zstd.dstream = ZSTD_createDStream();
if (fp->zstd.dstream == NULL)
@@ -825,15 +791,19 @@ cfopen(const char *path, const char *mode, int compression)
break;
#endif
- default:
+ case COMPR_ALG_NONE:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
-
return fp;
+ break;
+
+ default:
+ /* should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -944,7 +914,7 @@ cfwrite(const void *ptr, int size, cfp *fp)
else
#endif
- return fwrite(ptr, 1, size, fp->uncompressedfp);
+ return fwrite(ptr, 1, size, fp->uncompressedfp);
}
int
@@ -1005,7 +975,7 @@ cfgets(cfp *fp, char *buf, int len)
buf[res] = 0;
if (strchr(buf, '\n'))
*strchr(buf, '\n') = '\0';
- return buf;
+ return res > 0 ? buf : 0;
}
else
#endif
@@ -1122,5 +1092,22 @@ hasSuffix(const char *filename, const char *suffix)
suffix,
suffixlen) == 0;
}
-
#endif
+
+/*
+ * Return a string for the given AH's compression.
+ * The string is statically allocated.
+ */
+const char *
+compress_suffix(Compress *compression)
+{
+ switch (compression->alg)
+ {
+ case COMPR_ALG_LIBZ:
+ return ".gz";
+ case COMPR_ALG_ZSTD:
+ return ".zst";
+ default:
+ return "";
+ }
+}
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index f0ce06f176..2c073676eb 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -21,13 +21,6 @@
#define ZLIB_OUT_SIZE 4096
#define ZLIB_IN_SIZE 4096
-typedef enum
-{
- COMPR_ALG_NONE,
- COMPR_ALG_LIBZ,
- COMPR_ALG_ZSTD,
-} CompressionAlgorithm;
-
/* Prototype for callback function to WriteDataToArchive() */
typedef void (*WriteFunc) (ArchiveHandle *AH, const char *buf, size_t len);
@@ -47,8 +40,8 @@ typedef size_t (*ReadFunc) (ArchiveHandle *AH, char **buf, size_t *buflen);
/* struct definition appears in compress_io.c */
typedef struct CompressorState CompressorState;
-extern CompressorState *AllocateCompressor(int compression, WriteFunc writeF);
-extern void ReadDataFromArchive(ArchiveHandle *AH, int compression,
+extern CompressorState *AllocateCompressor(Compress *compression, WriteFunc writeF);
+extern void ReadDataFromArchive(ArchiveHandle *AH,
ReadFunc readF);
extern void WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
const void *data, size_t dLen);
@@ -57,9 +50,9 @@ extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
typedef struct cfp cfp;
-extern cfp *cfopen(const char *path, const char *mode, int compression);
-extern cfp *cfopen_read(const char *path, const char *mode, int compression);
-extern cfp *cfopen_write(const char *path, const char *mode, int compression);
+extern cfp *cfopen(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_read(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_write(const char *path, const char *mode, Compress *compression);
extern int cfread(void *ptr, int size, cfp *fp);
extern int cfwrite(const void *ptr, int size, cfp *fp);
extern int cfgetc(cfp *fp);
@@ -68,4 +61,7 @@ extern int cfclose(cfp *fp);
extern int cfeof(cfp *fp);
extern const char *get_cfp_error(cfp *fp);
+/* also used by tar */
+extern const char * compress_suffix(Compress *compression);
+
#endif
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 9d0056a569..1c1954dd1a 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -72,6 +72,21 @@ typedef struct _connParams
char *override_dbname;
} ConnParams;
+typedef enum
+{
+ COMPR_ALG_DEFAULT = -1,
+ COMPR_ALG_NONE,
+ COMPR_ALG_LIBZ,
+ COMPR_ALG_ZSTD,
+} CompressionAlgorithm;
+
+typedef struct Compress {
+ CompressionAlgorithm alg;
+ int level; // XXX: default
+ bool zstdlong;
+} Compress;
+
+
typedef struct _restoreOptions
{
int createDB; /* Issue commands to create the database */
@@ -125,7 +140,7 @@ typedef struct _restoreOptions
int noDataForFailedTables;
int exit_on_error;
- int compression;
+ Compress compression;
int suppressDumpWarnings; /* Suppress output of WARNING entries
* to stderr */
bool single_txn;
@@ -281,7 +296,7 @@ extern Archive *OpenArchive(const char *FileSpec, const ArchiveFormat fmt);
/* Create a new archive */
extern Archive *CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker);
/* The --list option */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1f82c6499b..2f1d59ce7a 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -70,7 +70,7 @@ typedef struct _parallelReadyList
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr);
static void _getObjectDescription(PQExpBuffer buf, TocEntry *te);
static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData);
@@ -98,7 +98,7 @@ static int _discoverArchiveFormat(ArchiveHandle *AH);
static int RestoringToDB(ArchiveHandle *AH);
static void dump_lo_buf(ArchiveHandle *AH);
static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
-static void SetOutput(ArchiveHandle *AH, const char *filename, int compression);
+static void SetOutput(ArchiveHandle *AH, const char *filename, Compress *compress);
static OutputContext SaveOutput(ArchiveHandle *AH);
static void RestoreOutput(ArchiveHandle *AH, OutputContext savedContext);
@@ -238,7 +238,7 @@ setupRestoreWorker(Archive *AHX)
/* Public */
Archive *
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker)
{
@@ -253,7 +253,9 @@ CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
Archive *
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
{
- ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, true, archModeRead, setupRestoreWorker);
+ Compress compress = {0};
+ ArchiveHandle *AH = _allocAH(FileSpec, fmt, &compress, true, archModeRead,
+ setupRestoreWorker);
return (Archive *) AH;
}
@@ -382,7 +384,7 @@ RestoreArchive(Archive *AHX)
* Make sure we won't need (de)compression we haven't got
*/
#ifndef HAVE_LIBZ
- if (AH->compression != 0 && AH->PrintTocDataPtr != NULL)
+ if (AH->compression.alg != COMPR_ALG_NONE && AH->PrintTocDataPtr != NULL)
{
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
@@ -457,8 +459,8 @@ RestoreArchive(Archive *AHX)
* Setup the output file if necessary.
*/
sav = SaveOutput(AH);
- if (ropt->filename || ropt->compression)
- SetOutput(AH, ropt->filename, ropt->compression);
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
+ SetOutput(AH, ropt->filename, &ropt->compression);
ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
@@ -738,7 +740,7 @@ RestoreArchive(Archive *AHX)
*/
AH->stage = STAGE_FINALIZING;
- if (ropt->filename || ropt->compression)
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
RestoreOutput(AH, sav);
if (ropt->useDB)
@@ -1121,10 +1123,11 @@ PrintTOCSummary(Archive *AHX)
OutputContext sav;
const char *fmtName;
char stamp_str[64];
+ Compress nocompression = {0};
sav = SaveOutput(AH);
if (ropt->filename)
- SetOutput(AH, ropt->filename, 0 /* no compression */ );
+ SetOutput(AH, ropt->filename, &nocompression);
if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
localtime(&AH->createDate)) == 0)
@@ -1133,7 +1136,7 @@ PrintTOCSummary(Archive *AHX)
ahprintf(AH, ";\n; Archive created at %s\n", stamp_str);
ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %d\n",
sanitize_line(AH->archdbname, false),
- AH->tocCount, AH->compression);
+ AH->tocCount, AH->compression.alg);
switch (AH->format)
{
@@ -1487,7 +1490,7 @@ archprintf(Archive *AH, const char *fmt,...)
*******************************/
static void
-SetOutput(ArchiveHandle *AH, const char *filename, int compression)
+SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
{
int fn;
@@ -1510,12 +1513,12 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
/* If compression explicitly requested, use gzopen */
#ifdef HAVE_LIBZ
- if (compression != 0)
+ if (compression->alg != COMPR_ALG_NONE)
{
char fmode[14];
/* Don't use PG_BINARY_x since this is zlib */
- sprintf(fmode, "wb%d", compression);
+ sprintf(fmode, "wb%d", compression->level);
if (fn >= 0)
AH->OF = gzdopen(dup(fn), fmode);
else
@@ -2259,7 +2262,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
*/
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr)
{
ArchiveHandle *AH;
@@ -2310,7 +2313,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
AH->toc->prev = AH->toc;
AH->mode = mode;
- AH->compression = compression;
+ AH->compression = *compression;
AH->dosync = dosync;
memset(&(AH->sqlparse), 0, sizeof(AH->sqlparse));
@@ -2325,7 +2328,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
* Force stdin/stdout into binary mode if that is what we are using.
*/
#ifdef WIN32
- if ((fmt != archNull || compression != 0) &&
+ if ((fmt != archNull || compression->alg != COMPR_ALG_NONE) &&
(AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0))
{
if (mode == archModeWrite)
@@ -3741,7 +3744,7 @@ WriteHead(ArchiveHandle *AH)
AH->WriteBytePtr(AH, AH->intSize);
AH->WriteBytePtr(AH, AH->offSize);
AH->WriteBytePtr(AH, AH->format);
- WriteInt(AH, AH->compression);
+ WriteInt(AH, AH->compression.alg);
crtm = *localtime(&AH->createDate);
WriteInt(AH, crtm.tm_sec);
WriteInt(AH, crtm.tm_min);
@@ -3816,15 +3819,15 @@ ReadHead(ArchiveHandle *AH)
if (AH->version >= K_VERS_1_2)
{
if (AH->version < K_VERS_1_4)
- AH->compression = AH->ReadBytePtr(AH);
+ AH->compression.alg = AH->ReadBytePtr(AH);
else
- AH->compression = ReadInt(AH);
+ AH->compression.alg = ReadInt(AH);
}
else
- AH->compression = Z_DEFAULT_COMPRESSION;
+ AH->compression.alg = Z_DEFAULT_COMPRESSION;
#ifndef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
pg_log_warning("archive is compressed, but this installation does not support compression -- no data will be available");
#endif
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 1a229ebedb..641ba2d043 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -47,8 +47,6 @@
#define GZWRITE(p, s, n, fh) (fwrite(p, s, n, fh) * (s))
#define GZREAD(p, s, n, fh) fread(p, s, n, fh)
#define GZEOF(fh) feof(fh)
-/* this is just the redefinition of a libz constant */
-#define Z_DEFAULT_COMPRESSION (-1)
typedef struct _z_stream
{
@@ -62,7 +60,6 @@ typedef z_stream *z_streamp;
#ifdef HAVE_LIBZSTD
#include <zstd.h>
-#define ZSTD_COMPRESSION -2
#endif /* HAVE_LIBZSTD */
/* Data block types */
@@ -334,13 +331,7 @@ struct _archiveHandle
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
- int compression; /*---------
- * Compression requested on open
- * Possible values for compression:
- * -2 ZSTD_COMPRESSION
- * -1 Z_DEFAULT_COMPRESSION
- * 0 COMPRESSION_NONE
- * 1-9 levels for gzip compression */
+ Compress compression; /* Compression requested on open */
bool dosync; /* data requested to be synced on sight */
ArchiveMode mode; /* File mode - r or w */
void *formatData; /* Header data specific to file format */
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..55a887a236 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -298,7 +298,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
_WriteByte(AH, BLK_DATA); /* Block type */
WriteInt(AH, te->dumpId); /* For sanity check */
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -377,7 +377,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
WriteInt(AH, oid);
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -566,7 +566,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
static void
_PrintData(ArchiveHandle *AH)
{
- ReadDataFromArchive(AH, AH->compression, _CustomReadFunc);
+ ReadDataFromArchive(AH, _CustomReadFunc);
}
static void
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 6e55cb425e..dfa36d5dc1 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -202,7 +202,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
setFilePath(AH, fname, "toc.dat");
- tocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
+ tocFH = cfopen_read(fname, PG_BINARY_R, &AH->compression);
if (tocFH == NULL)
fatal("could not open input file \"%s\": %m", fname);
@@ -327,7 +327,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
setFilePath(AH, fname, tctx->filename);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -388,12 +388,12 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!filename)
return;
- cfp = cfopen_read(filename, PG_BINARY_R, AH->compression);
+ cfp = cfopen_read(filename, PG_BINARY_R, &AH->compression);
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
- buf = pg_malloc(ZLIB_OUT_SIZE);
+ buf = pg_malloc(ZLIB_OUT_SIZE); // XXX
buflen = ZLIB_OUT_SIZE;
while ((cnt = cfread(buf, buflen, cfp)))
@@ -435,12 +435,13 @@ _LoadBlobs(ArchiveHandle *AH)
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
char line[MAXPGPATH];
+ Compress nocompression = {0};
StartRestoreBlobs(AH);
setFilePath(AH, fname, "blobs.toc");
- ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, AH->compression);
+ ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open large object TOC file \"%s\" for input: %m",
@@ -573,6 +574,7 @@ _CloseArchive(ArchiveHandle *AH)
{
cfp *tocFH;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "toc.dat");
@@ -580,7 +582,7 @@ _CloseArchive(ArchiveHandle *AH)
ctx->pstate = ParallelBackupStart(AH);
/* The TOC is always created uncompressed */
- tocFH = cfopen_write(fname, PG_BINARY_W, 0);
+ tocFH = cfopen_write(fname, PG_BINARY_W, &nocompression);
if (tocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
ctx->dataFH = tocFH;
@@ -639,11 +641,12 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
{
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "blobs.toc");
/* The blob TOC file is never compressed */
- ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
+ ctx->blobsTocFH = cfopen_write(fname, "ab", &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -661,7 +664,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 54e708875c..5c9d17bae7 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -39,6 +39,7 @@
#include "pg_backup_archiver.h"
#include "pg_backup_tar.h"
#include "pg_backup_utils.h"
+#include "compress_io.h"
#include "pgtar.h"
static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
@@ -196,10 +197,10 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
/*
* We don't support compression because reading the files back is not
- * possible since gzdopen uses buffered IO which totally screws file
+ * possible since gzdopen uses buffered IO which totally screws file XXX
* positioning.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
fatal("compression is not supported by tar archive format");
}
else
@@ -254,14 +255,8 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
if (te->dataDumper != NULL)
{
-#ifdef HAVE_LIBZ
- if (AH->compression == 0)
- sprintf(fn, "%d.dat", te->dumpId);
- else
- sprintf(fn, "%d.dat.gz", te->dumpId);
-#else
- sprintf(fn, "%d.dat", te->dumpId);
-#endif
+ const char *suffix = compress_suffix(&AH->compression);
+ sprintf(fn, "%d.dat%s", te->dumpId, suffix);
ctx->filename = pg_strdup(fn);
}
else
@@ -352,7 +347,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression == 0)
+ if (AH->compression.alg == COMPR_ALG_NONE)
tm->nFH = ctx->tarFH;
else
fatal("compression is not supported by tar archive format");
@@ -413,9 +408,9 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
{
- sprintf(fmode, "wb%d", AH->compression);
+ sprintf(fmode, "wb%d", AH->compression.level);
tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
if (tm->zFH == NULL)
fatal("could not open temporary file");
@@ -443,7 +438,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
/*
* Close the GZ file since we dup'd. This will flush the buffers.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
if (GZCLOSE(th->zFH) != 0)
fatal("could not close tar member");
@@ -868,7 +863,7 @@ _CloseArchive(ArchiveHandle *AH)
memcpy(ropt, AH->public.ropt, sizeof(RestoreOptions));
ropt->filename = NULL;
ropt->dropSchema = 1;
- ropt->compression = 0;
+ ropt->compression.alg = COMPR_ALG_NONE;
ropt->superuser = NULL;
ropt->suppressDumpWarnings = true;
@@ -952,16 +947,12 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
char fname[255];
- char *sfx;
+ const char *sfx;
if (oid == 0)
fatal("invalid OID for large object (%u)", oid);
- if (AH->compression != 0)
- sfx = ".gz";
- else
- sfx = "";
-
+ sfx = compress_suffix(&AH->compression);
sprintf(fname, "blob_%u.dat%s", oid, sfx);
tarPrintf(ctx->blobToc, "%u %s\n", oid, fname);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f21eb021c7..5f80d05716 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -59,6 +59,7 @@
#include "getopt_long.h"
#include "libpq/libpq-fs.h"
#include "parallel.h"
+#include "compress_io.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "pg_dump.h"
@@ -297,6 +298,111 @@ static void setupDumpWorker(Archive *AHX);
static TableInfo *getRootTableInfo(TableInfo *tbinfo);
+/* Parse the string into compression options */
+static void
+parse_compression(const char *optarg, Compress *compress)
+{
+ if (optarg[0] == '0' && optarg[1] == '\0')
+ compress->alg = COMPR_ALG_NONE;
+ else if ((optarg[0] > '0' && optarg[0] <= '9') ||
+ optarg[0] == '-')
+ {
+ compress->alg = COMPR_ALG_LIBZ;
+ compress->level = atoi(optarg);
+ if (optarg[1] != '\0')
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+ }
+ else
+ {
+ /* Parse a more flexible string like level=3 alg=zlib opts=long */
+ for (;;)
+ {
+ char *eq = strchr(optarg, '=');
+ int len;
+
+ if (eq == NULL)
+ {
+ pg_log_error("compression options must be key=value: %s", optarg);
+ exit_nicely(1);
+ }
+
+ len = eq - optarg;
+ if (strncmp(optarg, "alg", len) == 0)
+ {
+ if (strchr(eq, ' '))
+ len = strchr(eq, ' ') - eq - 1;
+ else
+ len = strlen(eq) - len;
+ if (strncmp(1+eq, "zlib", len) == 0 ||
+ strncmp(1+eq, "libz", len) == 0)
+ compress->alg = COMPR_ALG_LIBZ;
+ else if (strncmp(1+eq, "zstd", len) == 0)
+ compress->alg = COMPR_ALG_ZSTD;
+ else
+ {
+ pg_log_error("unknown compression algorithm: %s", 1+eq);
+ exit_nicely(1);
+ }
+ }
+ else if (strncmp(optarg, "level", len) == 0)
+ compress->level = atoi(1+eq);
+ else if (strncmp(optarg, "opt", len) == 0)
+ {
+ if (strchr(eq, ' '))
+ len = strchr(eq, ' ') - eq - 1;
+ else
+ len = strlen(eq) - len;
+ if (strncmp(1+eq, "zstdlong", len) == 0)
+ compress->zstdlong = true;
+ else
+ {
+ pg_log_error("unknown compression option: %s", 1+eq);
+ exit_nicely(1);
+ }
+ }
+ else
+ {
+ pg_log_error("unknown compression setting: %s", optarg);
+ exit_nicely(1);
+ }
+
+ optarg = strchr(eq, ' ');
+ if (!optarg++)
+ break;
+ }
+
+ /* zstd will check its own compression level later */
+ if (compress->alg != COMPR_ALG_ZSTD)
+ {
+ if (compress->level < 0 || compress->level > 9)
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+ if (compress->zstdlong)
+ {
+ pg_log_error("compression option not supported with this algorithm");
+ exit_nicely(1);
+ }
+ }
+
+ /* XXX: 0 means "unset" */
+ if (compress->level == 0)
+ {
+ const int default_compress_level[] = {
+ 0, /* COMPR_ALG_NONE */
+ Z_DEFAULT_COMPRESSION, /* COMPR_ALG_ZLIB */
+ ZSTD_CLEVEL_DEFAULT, /* COMPR_ALG_ZSTD */
+ };
+
+ compress->level = default_compress_level[compress->alg];
+ }
+ }
+}
+
int
main(int argc, char **argv)
{
@@ -319,7 +425,7 @@ main(int argc, char **argv)
char *use_role = NULL;
long rowsPerInsert;
int numWorkers = 1;
- int compressLevel = -1;
+ Compress compress = { .alg = COMPR_ALG_DEFAULT };
int plainText = 0;
ArchiveFormat archiveFormat = archUnknown;
ArchiveMode archiveMode;
@@ -532,13 +638,7 @@ main(int argc, char **argv)
break;
case 'Z': /* Compression Level */
- compressLevel = atoi(optarg);
- if ((compressLevel < 0 || compressLevel > 9) &&
- compressLevel != -2)
- {
- pg_log_error("compression level must be in range 0..9");
- exit_nicely(1);
- }
+ parse_compression(optarg, &compress);
break;
case 0:
@@ -680,20 +780,40 @@ main(int argc, char **argv)
plainText = 1;
/* Custom and directory formats are compressed by default, others not */
- if (compressLevel == -1)
+ if (compress.alg == COMPR_ALG_DEFAULT)
{
-#ifdef HAVE_LIBZ
if (archiveFormat == archCustom || archiveFormat == archDirectory)
- compressLevel = Z_DEFAULT_COMPRESSION;
- else
+ {
+#ifdef HAVE_LIBZ
+ compress.alg = COMPR_ALG_LIBZ;
+ compress.level = Z_DEFAULT_COMPRESSION;
+#endif
+#ifdef HAVE_LIBZSTD
+ compress.alg = COMPR_ALG_ZSTD; // Set default for testing purposes
+ compress.level = ZSTD_CLEVEL_DEFAULT;
#endif
- compressLevel = 0;
+ }
+ else
+ {
+ compress.alg = COMPR_ALG_NONE;
+ compress.level = 0;
+ }
}
#ifndef HAVE_LIBZ
- if (compressLevel != 0)
+ if (compress.alg == COMPR_ALG_LIBZ)
+ {
pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
- compressLevel = 0;
+ compress.alg = 0;
+ }
+#endif
+
+#ifndef HAVE_LIBZ
+ if (compress.alg == COMPR_ALG_ZSTD)
+ {
+ pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
+ compress.alg = 0;
+ }
#endif
/*
@@ -724,7 +844,7 @@ main(int argc, char **argv)
fatal("option --index-collation-versions-unknown only works in binary upgrade mode");
/* Open the output file */
- fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
+ fout = CreateArchive(filename, archiveFormat, &compress, dosync,
archiveMode, setupDumpWorker);
/* Make dump options accessible right away */
@@ -958,10 +1078,7 @@ main(int argc, char **argv)
ropt->sequence_data = dopt.sequence_data;
ropt->binary_upgrade = dopt.binary_upgrade;
- if (compressLevel == -1)
- ropt->compression = 0;
- else
- ropt->compression = compressLevel;
+ ropt->compression = compress;
ropt->suppressDumpWarnings = true; /* We've already shown them */
--
2.17.0
0005-union-with-a-CompressionAlgorithm-alg.patchtext/x-diff; charset=us-asciiDownload
From bc6bc514d784da4cab7262c8386b0b8ed29ed02f Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 21 Dec 2020 00:11:43 -0600
Subject: [PATCH 5/7] union{} with a CompressionAlgorithm alg
---
src/bin/pg_dump/compress_io.c | 218 ++++++++++++++++------------------
1 file changed, 105 insertions(+), 113 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 421b8e04cb..06005c3fba 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -619,23 +619,27 @@ WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
*/
struct cfp
{
- FILE *uncompressedfp;
+ CompressionAlgorithm alg;
+
+ union {
+ FILE *fp;
+
#ifdef HAVE_LIBZ
- gzFile compressedfp;
+ gzFile gzfp;
#endif
-#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
- /* This is a normal file to which we read/write compressed data */
- struct {
- FILE *fp;
- // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
- ZSTD_DStream *dstream;
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
- } zstd;
+#ifdef HAVE_LIBZSTD
+ struct {
+ /* This is a normal file to which we read/write compressed data */
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
#endif
-
+ } u;
};
#ifdef HAVE_LIBZ
@@ -726,13 +730,7 @@ cfopen(const char *path, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc(sizeof(cfp));
- fp->uncompressedfp = NULL;
-#ifdef HAVE_LIBZ
- fp->compressedfp = NULL;
-#endif
-#ifdef HAVE_LIBZSTD
- fp->zstd.fp = NULL;
-#endif
+ fp->alg = compression->alg;
switch (compression->alg)
{
@@ -745,15 +743,15 @@ cfopen(const char *path, const char *mode, Compress *compression)
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
mode, compression->level);
- fp->compressedfp = gzopen(path, mode_compression);
+ fp->u.gzfp = gzopen(path, mode_compression);
}
else
{
/* don't specify a level, just use the zlib default */
- fp->compressedfp = gzopen(path, mode);
+ fp->u.gzfp = gzopen(path, mode);
}
- if (fp->compressedfp == NULL)
+ if (fp->u.gzfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -765,26 +763,26 @@ cfopen(const char *path, const char *mode, Compress *compression)
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- fp->zstd.fp = fopen(path, mode);
- if (fp->zstd.fp == NULL)
+ fp->u.zstd.fp = fopen(path, mode);
+ if (fp->u.zstd.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
else if (strchr(mode, 'w'))
{
- fp->zstd.dstream = NULL;
- fp->zstd.output.size = ZSTD_CStreamOutSize();
- fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
- fp->zstd.cstream = ZstdCStreamParams(compression);
+ fp->u.zstd.dstream = NULL;
+ fp->u.zstd.output.size = ZSTD_CStreamOutSize();
+ fp->u.zstd.output.dst = pg_malloc0(fp->u.zstd.output.size);
+ fp->u.zstd.cstream = ZstdCStreamParams(compression);
}
else if (strchr(mode, 'r'))
{
- fp->zstd.cstream = NULL;
- fp->zstd.input.size = ZSTD_DStreamInSize();
- fp->zstd.input.src = pg_malloc0(fp->zstd.input.size);
- fp->zstd.dstream = ZSTD_createDStream();
- if (fp->zstd.dstream == NULL)
+ fp->u.zstd.cstream = NULL;
+ fp->u.zstd.input.size = ZSTD_DStreamInSize();
+ fp->u.zstd.input.src = pg_malloc0(fp->u.zstd.input.size);
+ fp->u.zstd.dstream = ZSTD_createDStream();
+ if (fp->u.zstd.dstream == NULL)
fatal("could not initialize compression library");
} // XXX else: bad mode
return fp;
@@ -792,8 +790,8 @@ cfopen(const char *path, const char *mode, Compress *compression)
#endif
case COMPR_ALG_NONE:
- fp->uncompressedfp = fopen(path, mode);
- if (fp->uncompressedfp == NULL)
+ fp->u.fp = fopen(path, mode);
+ if (fp->u.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -817,26 +815,26 @@ cfread(void *ptr, int size, cfp *fp)
return 0;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzread(fp->compressedfp, ptr, size);
- if (ret != size && !gzeof(fp->compressedfp))
+ ret = gzread(fp->u.gzfp, ptr, size);
+ if (ret != size && !gzeof(fp->u.gzfp))
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
fatal("could not read from input file: %s",
errnum == Z_ERRNO ? strerror(errno) : errmsg);
}
+ return ret;
}
- else
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t input_size = ZSTD_DStreamInSize();
size_t res, cnt;
@@ -845,7 +843,7 @@ cfread(void *ptr, int size, cfp *fp)
output->pos = 0;
/* read compressed data */
- while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp)))
+ while ((cnt = fread(unconstify(void *, input->src), 1, input_size, fp->u.zstd.fp)))
{
input->size = cnt;
input->pos = 0;
@@ -853,7 +851,7 @@ cfread(void *ptr, int size, cfp *fp)
for ( ; input->pos < input->size; )
{
/* decompress */
- res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ res = ZSTD_decompressStream(fp->u.zstd.dstream, output, input);
if (res == 0 || output->pos == output->size)
break;
if (ZSTD_isError(res))
@@ -864,16 +862,13 @@ cfread(void *ptr, int size, cfp *fp)
break; /* We read all the data that fits */
}
- ret = output->pos;
+ return output->pos;
}
- else
#endif
- {
- ret = fread(ptr, 1, size, fp->uncompressedfp);
- if (ret != size && !feof(fp->uncompressedfp))
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
+ ret = fread(ptr, 1, size, fp->u.fp);
+ if (ret != size && !feof(fp->u.fp))
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -881,16 +876,16 @@ int
cfwrite(const void *ptr, int size, cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzwrite(fp->compressedfp, ptr, size);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzwrite(fp->u.gzfp, ptr, size);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
size_t res, cnt;
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
input->src = ptr;
input->size = size;
@@ -900,21 +895,20 @@ cfwrite(const void *ptr, int size, cfp *fp)
while (input->pos != input->size)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_continue);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
}
return size;
}
- else
#endif
- return fwrite(ptr, 1, size, fp->uncompressedfp);
+ return fwrite(ptr, 1, size, fp->u.fp);
}
int
@@ -923,39 +917,38 @@ cfgetc(cfp *fp)
int ret;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzgetc(fp->compressedfp);
+ ret = gzgetc(fp->u.gzfp);
if (ret == EOF)
{
- if (!gzeof(fp->compressedfp))
+ if (!gzeof(fp->u.gzfp))
fatal("could not read from input file: %s", strerror(errno));
else
fatal("could not read from input file: end of file");
}
+ return ret;
}
- else
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
if (cfread(&ret, 1, fp) != 1)
{
- if (feof(fp->zstd.fp))
+ if (feof(fp->u.zstd.fp))
fatal("could not read from input file: end of file");
else
fatal("could not read from input file: %s", strerror(errno));
}
fprintf(stderr, "cfgetc %d\n", ret);
+ return ret;
}
#endif
- {
- ret = fgetc(fp->uncompressedfp);
- if (ret == EOF)
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
-
+ ret = fgetc(fp->u.fp);
+ if (ret == EOF)
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -963,12 +956,12 @@ char *
cfgets(cfp *fp, char *buf, int len)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzgets(fp->compressedfp, buf, len);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzgets(fp->u.gzfp, buf, len);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
int res;
res = cfread(buf, len, fp);
@@ -977,9 +970,9 @@ cfgets(cfp *fp, char *buf, int len)
*strchr(buf, '\n') = '\0';
return res > 0 ? buf : 0;
}
- else
#endif
- return fgets(buf, len, fp->uncompressedfp);
+
+ return fgets(buf, len, fp->u.fp);
}
int
@@ -993,56 +986,55 @@ cfclose(cfp *fp)
return EOF;
}
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- result = gzclose(fp->compressedfp);
- fp->compressedfp = NULL;
+ result = gzclose(fp->u.gzfp);
+ fp->u.gzfp = NULL;
+ return result;
}
- else
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t res, cnt;
- if (fp->zstd.cstream)
+ if (fp->u.zstd.cstream)
{
for (;;)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_end);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
if (res == 0)
break;
}
- ZSTD_freeCStream(fp->zstd.cstream);
- pg_free(fp->zstd.output.dst);
+ ZSTD_freeCStream(fp->u.zstd.cstream);
+ pg_free(fp->u.zstd.output.dst);
}
- if (fp->zstd.dstream)
+ if (fp->u.zstd.dstream)
{
- ZSTD_freeDStream(fp->zstd.dstream);
- pg_free(unconstify(void *, fp->zstd.input.src));
+ ZSTD_freeDStream(fp->u.zstd.dstream);
+ pg_free(unconstify(void *, fp->u.zstd.input.src));
}
- result = fclose(fp->zstd.fp);
- fp->zstd.fp = NULL;
+ result = fclose(fp->u.zstd.fp);
+ fp->u.zstd.fp = NULL;
+ return result;
}
- else
#endif
- {
- result = fclose(fp->uncompressedfp);
- fp->uncompressedfp = NULL;
- }
- free_keep_errno(fp);
+ result = fclose(fp->u.fp);
+ fp->u.fp = NULL;
+ free_keep_errno(fp);
return result;
}
@@ -1050,26 +1042,26 @@ int
cfeof(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzeof(fp->compressedfp);
- else
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzeof(fp->u.gzfp);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
- return feof(fp->zstd.fp);
- else
+ if (fp->alg == COMPR_ALG_ZSTD)
+ return feof(fp->u.zstd.fp);
#endif
- return feof(fp->uncompressedfp);
+
+ return feof(fp->u.fp);
}
const char *
get_cfp_error(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
if (errnum != Z_ERRNO)
return errmsg;
--
2.17.0
0006-Move-zlib-into-the-union.patchtext/x-diff; charset=us-asciiDownload
From 15cd65b4d650009cf18f48357e9dfe9173d32446 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 11 Dec 2020 22:22:31 -0600
Subject: [PATCH 6/7] Move zlib into the union{}
---
src/bin/pg_dump/compress_io.c | 54 ++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 26 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 06005c3fba..a94efbea4e 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -67,23 +67,25 @@ struct CompressorState
CompressionAlgorithm comprAlg;
WriteFunc writeF;
+ union {
#ifdef HAVE_LIBZ
- z_streamp zp;
- char *zlibOut;
- size_t zlibOutSize;
+ struct {
+ z_streamp zp;
+ char *zlibOut;
+ size_t zlibOutSize;
+ } zlib;
#endif
#ifdef HAVE_LIBZSTD
- union {
+ /* This is used for compression but not decompression */
struct {
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
// XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
+ ZSTD_CStream *cstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
} zstd;
- } u;
#endif
-
+ } u;
};
/* Routines that support zlib compressed data I/O */
@@ -420,7 +422,7 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
- zp = cs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
+ zp = cs->u.zlib.zp = (z_streamp) pg_malloc(sizeof(z_stream));
zp->zalloc = Z_NULL;
zp->zfree = Z_NULL;
zp->opaque = Z_NULL;
@@ -430,22 +432,22 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
* actually allocate one extra byte because some routines want to append a
* trailing zero byte to the zlib output.
*/
- cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
- cs->zlibOutSize = ZLIB_OUT_SIZE;
+ cs->u.zlib.zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
+ cs->u.zlib.zlibOutSize = ZLIB_OUT_SIZE;
if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
/* Just be paranoid - maybe End is called after Start, with no Write */
- zp->next_out = (void *) cs->zlibOut;
- zp->avail_out = cs->zlibOutSize;
+ zp->next_out = (void *) cs->u.zlib.zlibOut;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
static void
EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
{
- z_streamp zp = cs->zp;
+ z_streamp zp = cs->u.zlib.zp;
zp->next_in = NULL;
zp->avail_in = 0;
@@ -456,23 +458,23 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
if (deflateEnd(zp) != Z_OK)
fatal("could not close compression stream: %s", zp->msg);
- free(cs->zlibOut);
- free(cs->zp);
+ free(cs->u.zlib.zlibOut);
+ free(cs->u.zlib.zp);
}
static void
DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
{
- z_streamp zp = cs->zp;
- char *out = cs->zlibOut;
+ z_streamp zp = cs->u.zlib.zp;
+ char *out = cs->u.zlib.zlibOut;
int res = Z_OK;
- while (cs->zp->avail_in != 0 || flush)
+ while (cs->u.zlib.zp->avail_in != 0 || flush)
{
res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
if (res == Z_STREAM_ERROR)
fatal("could not compress data: %s", zp->msg);
- if ((flush && (zp->avail_out < cs->zlibOutSize))
+ if ((flush && (zp->avail_out < cs->u.zlib.zlibOutSize))
|| (zp->avail_out == 0)
|| (zp->avail_in != 0)
)
@@ -482,18 +484,18 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
* chunk is the EOF marker in the custom format. This should never
* happen but...
*/
- if (zp->avail_out < cs->zlibOutSize)
+ if (zp->avail_out < cs->u.zlib.zlibOutSize)
{
/*
* Any write function should do its own error checking but to
* make sure we do a check here as well...
*/
- size_t len = cs->zlibOutSize - zp->avail_out;
+ size_t len = cs->u.zlib.zlibOutSize - zp->avail_out;
cs->writeF(AH, out, len);
}
zp->next_out = (void *) out;
- zp->avail_out = cs->zlibOutSize;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
if (res == Z_STREAM_END)
@@ -505,8 +507,8 @@ static void
WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen)
{
- cs->zp->next_in = (void *) unconstify(char *, data);
- cs->zp->avail_in = dLen;
+ cs->u.zlib.zp->next_in = (void *) unconstify(char *, data);
+ cs->u.zlib.zp->avail_in = dLen;
DeflateCompressorZlib(AH, cs, false);
}
--
2.17.0
On Mon, Dec 21, 2020 at 01:49:24PM -0600, Justin Pryzby wrote:
a big disadvantage of piping through zstd is that it's not identified as a
PGDMP file, and, /usr/bin/file on centos7 fails to even identify zstd by its
magic number..
Other reasons are that pg_dump |zstd >output.zst loses the exit status of
pg_dump, and that it's not "transparent" (one needs to type
"zstd -dq |pg_restore").
On Mon, Dec 21, 2020 at 08:32:35PM -0600, Justin Pryzby wrote:
On Mon, Dec 21, 2020 at 03:02:40PM -0500, Tom Lane wrote:
Justin Pryzby <pryzby@telsasoft.com> writes:
I found that our largest tables are 40% smaller and 20% faster to pipe
pg_dump -Fc -Z0 |zstd relative to native zlibThe patch might be a tad smaller if you hadn't included a core file in it.
About 89% smaller.
This also fixes the extension (.zst)
And fixes zlib default compression.
And a bunch of cleanup.
I rebased so the "typedef struct compression" patch is first and zstd on top of
that (say, in case someone wants to bikeshed about which compression algorithm
to support). And made a central struct with all the compression-specific info
to further isolate the compress-specific changes.
And handle compression of "plain" archive format.
And fix compilation for MSVC and make --without-zstd the default.
And fix cfgets() (which I think is actually unused code for the code paths for
compressed FP).
And add fix for pre-existing problem: ftello() on unseekable input.
I also started a patch to allow compression of "tar" format, but I didn't
include that here yet.
Note, there's currently several "compression" patches in CF app. This patch
seems to be independent of the others, but probably shouldn't be totally
uncoordinated (like adding lz4 in one and ztsd in another might be poor
execution).
https://commitfest.postgresql.org/31/2897/
- Faster pglz compression
https://commitfest.postgresql.org/31/2813/
- custom compression methods for toast
https://commitfest.postgresql.org/31/2773/
- libpq compression
--
Justin
Attachments:
0001-fix-preeexisting.patchtext/x-diff; charset=us-asciiDownload
From d2fc2673e19a95629edfe9a0f4ead75e1f1f2754 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 23 Dec 2020 23:56:54 -0600
Subject: [PATCH 01/20] fix!preeexisting
---
src/bin/pg_dump/compress_io.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 1417401086..6a428978d4 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -28,7 +28,7 @@
* The interface for reading an archive consists of just one function:
* ReadDataFromArchive. ReadDataFromArchive reads the whole compressed input
* stream, by repeatedly calling the given ReadFunc. ReadFunc returns the
- * compressed data chunk at a time, and ReadDataFromArchive decompresses it
+ * compressed data one chunk at a time, and ReadDataFromArchive decompresses it
* and passes the decompressed data to ahwrite(), until ReadFunc returns 0
* to signal EOF.
*
--
2.17.0
0002-Fix-broken-error-message-on-unseekable-input.patchtext/x-diff; charset=us-asciiDownload
From e8bb61dd633aefb2cc7a14887a15cc60e05cd9c5 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Tue, 22 Dec 2020 15:40:08 -0600
Subject: [PATCH 02/20] Fix broken error message on unseekable input..
pg_dump -Fd -f dir.dir
cat dir.dir/toc.dat | pg_restore -l # I realize this isn't how it's intended to be used
pg_restore: error: corrupt tar header found in PGDMP (expected 0, computed 18577) file position 18446744073709551615
See also 929c69aa19
---
src/bin/pg_dump/pg_backup_tar.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 54e708875c..61c9c87a9f 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -1280,12 +1280,14 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
if (chk != sum)
{
- char posbuf[32];
+ off_t off = ftello(ctx->tarFH);
- snprintf(posbuf, sizeof(posbuf), UINT64_FORMAT,
- (uint64) ftello(ctx->tarFH));
- fatal("corrupt tar header found in %s (expected %d, computed %d) file position %s",
- tag, sum, chk, posbuf);
+ if (off == -1)
+ fatal("corrupt tar header found in %s (expected %d, computed %d)",
+ tag, sum, chk);
+ else
+ fatal("corrupt tar header found in %s (expected %d, computed %d) file position " UINT64_FORMAT,
+ tag, sum, chk, off);
}
th->targetFile = pg_strdup(tag);
--
2.17.0
0003-Support-multiple-compression-algs-levels-opts.patchtext/x-diff; charset=us-asciiDownload
From bbf91d13a25bcb2f0718f40984362f81741ca66a Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Tue, 22 Dec 2020 00:23:43 -0600
Subject: [PATCH 03/20] Support multiple compression algs/levels/opts..
The existing implementation abtracts compressed and noncompressed I/O.
This preliminary commit is intended to also allow for multiple compression
algorithms.
---
src/bin/pg_dump/compress_io.c | 220 +++++++++++---------------
src/bin/pg_dump/compress_io.h | 19 +--
src/bin/pg_dump/pg_backup.h | 23 ++-
src/bin/pg_dump/pg_backup_archiver.c | 45 +++---
src/bin/pg_dump/pg_backup_archiver.h | 12 +-
src/bin/pg_dump/pg_backup_custom.c | 6 +-
src/bin/pg_dump/pg_backup_directory.c | 17 +-
src/bin/pg_dump/pg_backup_tar.c | 33 ++--
src/bin/pg_dump/pg_dump.c | 115 +++++++++++---
9 files changed, 272 insertions(+), 218 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 6a428978d4..db16fd33f2 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -74,12 +74,9 @@ struct CompressorState
#endif
};
-static void ParseCompressionOption(int compression, CompressionAlgorithm *alg,
- int *level);
-
/* Routines that support zlib compressed data I/O */
#ifdef HAVE_LIBZ
-static void InitCompressorZlib(CompressorState *cs, int level);
+static void InitCompressorZlib(CompressorState *cs, Compress *compress);
static void DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs,
bool flush);
static void ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF);
@@ -93,58 +90,36 @@ static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen);
-/*
- * Interprets a numeric 'compression' value. The algorithm implied by the
- * value (zlib or none at the moment), is returned in *alg, and the
- * zlib compression level in *level.
- */
-static void
-ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
-{
- if (compression == Z_DEFAULT_COMPRESSION ||
- (compression > 0 && compression <= 9))
- *alg = COMPR_ALG_LIBZ;
- else if (compression == 0)
- *alg = COMPR_ALG_NONE;
- else
- {
- fatal("invalid compression code: %d", compression);
- *alg = COMPR_ALG_NONE; /* keep compiler quiet */
- }
-
- /* The level is just the passed-in value. */
- if (level)
- *level = compression;
-}
-
/* Public interface routines */
/* Allocate a new compressor */
CompressorState *
-AllocateCompressor(int compression, WriteFunc writeF)
+AllocateCompressor(Compress *compression, WriteFunc writeF)
{
CompressorState *cs;
- CompressionAlgorithm alg;
- int level;
-
- ParseCompressionOption(compression, &alg, &level);
-
-#ifndef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
- fatal("not built with zlib support");
-#endif
cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
cs->writeF = writeF;
- cs->comprAlg = alg;
+ cs->comprAlg = compression->alg;
/*
* Perform compression algorithm specific initialization.
*/
+ Assert (compression->alg != COMPR_ALG_DEFAULT);
+ switch (compression->alg)
+ {
#ifdef HAVE_LIBZ
- if (alg == COMPR_ALG_LIBZ)
- InitCompressorZlib(cs, level);
+ case COMPR_ALG_LIBZ:
+ InitCompressorZlib(cs, compression);
+ break;
#endif
+ case COMPR_ALG_NONE:
+ /* Do nothing */
+ break;
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
+ }
return cs;
}
@@ -154,21 +129,21 @@ AllocateCompressor(int compression, WriteFunc writeF)
* out with ahwrite().
*/
void
-ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
+ReadDataFromArchive(ArchiveHandle *AH, ReadFunc readF)
{
- CompressionAlgorithm alg;
-
- ParseCompressionOption(compression, &alg, NULL);
-
- if (alg == COMPR_ALG_NONE)
- ReadDataFromArchiveNone(AH, readF);
- if (alg == COMPR_ALG_LIBZ)
+ switch (AH->compression.alg)
{
+ case COMPR_ALG_NONE:
+ ReadDataFromArchiveNone(AH, readF);
+ break;
#ifdef HAVE_LIBZ
+ case COMPR_ALG_LIBZ:
ReadDataFromArchiveZlib(AH, readF);
-#else
- fatal("not built with zlib support");
+ break;
#endif
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -181,16 +156,18 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
{
switch (cs->comprAlg)
{
- case COMPR_ALG_LIBZ:
#ifdef HAVE_LIBZ
+ case COMPR_ALG_LIBZ:
WriteDataToArchiveZlib(AH, cs, data, dLen);
-#else
- fatal("not built with zlib support");
-#endif
break;
+#endif
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
+
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
}
}
@@ -215,7 +192,7 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
*/
static void
-InitCompressorZlib(CompressorState *cs, int level)
+InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
@@ -232,7 +209,7 @@ InitCompressorZlib(CompressorState *cs, int level)
cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
cs->zlibOutSize = ZLIB_OUT_SIZE;
- if (deflateInit(zp, level) != Z_OK)
+ if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
@@ -424,9 +401,7 @@ struct cfp
#endif
};
-#ifdef HAVE_LIBZ
static int hasSuffix(const char *filename, const char *suffix);
-#endif
/* free() without changing errno; useful in several places below */
static void
@@ -442,34 +417,31 @@ free_keep_errno(void *p)
* Open a file for reading. 'path' is the file to open, and 'mode' should
* be either "r" or "rb".
*
- * If the file at 'path' does not exist, we append the ".gz" suffix (if 'path'
- * doesn't already have it) and try again. So if you pass "foo" as 'path',
+ * If the file at 'path' does not exist, we search with compressed suffix (if 'path'
+ * doesn't already have one) and try again. So if you pass "foo" as 'path',
* this will open either "foo" or "foo.gz".
*
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_read(const char *path, const char *mode)
+cfopen_read(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
-#ifdef HAVE_LIBZ
if (hasSuffix(path, ".gz"))
- fp = cfopen(path, mode, 1);
+ fp = cfopen(path, mode, compression);
else
-#endif
{
- fp = cfopen(path, mode, 0);
-#ifdef HAVE_LIBZ
+ fp = cfopen(path, mode, compression);
if (fp == NULL)
{
char *fname;
+ const char *suffix = compress_suffix(compression);
- fname = psprintf("%s.gz", path);
- fp = cfopen(fname, mode, 1);
+ fname = psprintf("%s%s", path, suffix);
+ fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
}
-#endif
}
return fp;
}
@@ -479,31 +451,26 @@ cfopen_read(const char *path, const char *mode)
* be a filemode as accepted by fopen() and gzopen() that indicates writing
* ("w", "wb", "a", or "ab").
*
- * If 'compression' is non-zero, a gzip compressed stream is opened, and
- * 'compression' indicates the compression level used. The ".gz" suffix
- * is automatically added to 'path' in that case.
+ * Use compression if specified.
+ * The appropriate suffix is automatically added to 'path' in that case.
*
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen_write(const char *path, const char *mode, int compression)
+cfopen_write(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
- if (compression == 0)
- fp = cfopen(path, mode, 0);
+ if (compression->alg == COMPR_ALG_NONE)
+ fp = cfopen(path, mode, compression);
else
{
-#ifdef HAVE_LIBZ
char *fname;
+ const char *suffix = compress_suffix(compression);
- fname = psprintf("%s.gz", path);
+ fname = psprintf("%s%s", path, suffix);
fp = cfopen(fname, mode, compression);
free_keep_errno(fname);
-#else
- fatal("not built with zlib support");
- fp = NULL; /* keep compiler quiet */
-#endif
}
return fp;
}
@@ -515,20 +482,21 @@ cfopen_write(const char *path, const char *mode, int compression)
* On failure, return NULL with an error code in errno.
*/
cfp *
-cfopen(const char *path, const char *mode, int compression)
+cfopen(const char *path, const char *mode, Compress *compression)
{
- cfp *fp = pg_malloc(sizeof(cfp));
+ cfp *fp = pg_malloc0(sizeof(cfp));
- if (compression != 0)
+ switch (compression->alg)
{
#ifdef HAVE_LIBZ
- if (compression != Z_DEFAULT_COMPRESSION)
+ case COMPR_ALG_LIBZ:
+ if (compression->level != Z_DEFAULT_COMPRESSION)
{
/* user has specified a compression level, so tell zlib to use it */
char mode_compression[32];
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
- mode, compression);
+ mode, compression->level);
fp->compressedfp = gzopen(path, mode_compression);
}
else
@@ -537,30 +505,27 @@ cfopen(const char *path, const char *mode, int compression)
fp->compressedfp = gzopen(path, mode);
}
- fp->uncompressedfp = NULL;
if (fp->compressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
-#else
- fatal("not built with zlib support");
-#endif
- }
- else
- {
-#ifdef HAVE_LIBZ
- fp->compressedfp = NULL;
+ return fp;
#endif
+
+ case COMPR_ALG_NONE:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
}
- }
+ return fp;
- return fp;
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
+ }
}
@@ -584,14 +549,13 @@ cfread(void *ptr, int size, cfp *fp)
fatal("could not read from input file: %s",
errnum == Z_ERRNO ? strerror(errno) : errmsg);
}
+ return ret;
}
- else
#endif
- {
- ret = fread(ptr, 1, size, fp->uncompressedfp);
- if (ret != size && !feof(fp->uncompressedfp))
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
+
+ ret = fread(ptr, 1, size, fp->uncompressedfp);
+ if (ret != size && !feof(fp->uncompressedfp))
+ READ_ERROR_EXIT(fp->uncompressedfp);
return ret;
}
@@ -601,9 +565,8 @@ cfwrite(const void *ptr, int size, cfp *fp)
#ifdef HAVE_LIBZ
if (fp->compressedfp)
return gzwrite(fp->compressedfp, ptr, size);
- else
#endif
- return fwrite(ptr, 1, size, fp->uncompressedfp);
+ return fwrite(ptr, 1, size, fp->uncompressedfp);
}
int
@@ -622,15 +585,12 @@ cfgetc(cfp *fp)
else
fatal("could not read from input file: end of file");
}
+ return ret;
}
- else
#endif
- {
- ret = fgetc(fp->uncompressedfp);
- if (ret == EOF)
- READ_ERROR_EXIT(fp->uncompressedfp);
- }
-
+ ret = fgetc(fp->uncompressedfp);
+ if (ret == EOF)
+ READ_ERROR_EXIT(fp->uncompressedfp);
return ret;
}
@@ -640,9 +600,8 @@ cfgets(cfp *fp, char *buf, int len)
#ifdef HAVE_LIBZ
if (fp->compressedfp)
return gzgets(fp->compressedfp, buf, len);
- else
#endif
- return fgets(buf, len, fp->uncompressedfp);
+ return fgets(buf, len, fp->uncompressedfp);
}
int
@@ -660,15 +619,13 @@ cfclose(cfp *fp)
{
result = gzclose(fp->compressedfp);
fp->compressedfp = NULL;
+ return result;
}
- else
#endif
- {
- result = fclose(fp->uncompressedfp);
- fp->uncompressedfp = NULL;
- }
- free_keep_errno(fp);
+ result = fclose(fp->uncompressedfp);
+ fp->uncompressedfp = NULL;
+ free_keep_errno(fp);
return result;
}
@@ -678,9 +635,9 @@ cfeof(cfp *fp)
#ifdef HAVE_LIBZ
if (fp->compressedfp)
return gzeof(fp->compressedfp);
- else
#endif
- return feof(fp->uncompressedfp);
+
+ return feof(fp->uncompressedfp);
}
const char *
@@ -699,7 +656,6 @@ get_cfp_error(cfp *fp)
return strerror(errno);
}
-#ifdef HAVE_LIBZ
static int
hasSuffix(const char *filename, const char *suffix)
{
@@ -714,4 +670,18 @@ hasSuffix(const char *filename, const char *suffix)
suffixlen) == 0;
}
-#endif
+/*
+ * Return a string for the given AH's compression.
+ * The string is statically allocated.
+ */
+const char *
+compress_suffix(Compress *compression)
+{
+ switch (compression->alg)
+ {
+ case COMPR_ALG_LIBZ:
+ return ".gz";
+ default:
+ return "";
+ }
+}
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index d2e6e1b854..2c073676eb 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -21,12 +21,6 @@
#define ZLIB_OUT_SIZE 4096
#define ZLIB_IN_SIZE 4096
-typedef enum
-{
- COMPR_ALG_NONE,
- COMPR_ALG_LIBZ
-} CompressionAlgorithm;
-
/* Prototype for callback function to WriteDataToArchive() */
typedef void (*WriteFunc) (ArchiveHandle *AH, const char *buf, size_t len);
@@ -46,8 +40,8 @@ typedef size_t (*ReadFunc) (ArchiveHandle *AH, char **buf, size_t *buflen);
/* struct definition appears in compress_io.c */
typedef struct CompressorState CompressorState;
-extern CompressorState *AllocateCompressor(int compression, WriteFunc writeF);
-extern void ReadDataFromArchive(ArchiveHandle *AH, int compression,
+extern CompressorState *AllocateCompressor(Compress *compression, WriteFunc writeF);
+extern void ReadDataFromArchive(ArchiveHandle *AH,
ReadFunc readF);
extern void WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
const void *data, size_t dLen);
@@ -56,9 +50,9 @@ extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
typedef struct cfp cfp;
-extern cfp *cfopen(const char *path, const char *mode, int compression);
-extern cfp *cfopen_read(const char *path, const char *mode);
-extern cfp *cfopen_write(const char *path, const char *mode, int compression);
+extern cfp *cfopen(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_read(const char *path, const char *mode, Compress *compression);
+extern cfp *cfopen_write(const char *path, const char *mode, Compress *compression);
extern int cfread(void *ptr, int size, cfp *fp);
extern int cfwrite(const void *ptr, int size, cfp *fp);
extern int cfgetc(cfp *fp);
@@ -67,4 +61,7 @@ extern int cfclose(cfp *fp);
extern int cfeof(cfp *fp);
extern const char *get_cfp_error(cfp *fp);
+/* also used by tar */
+extern const char * compress_suffix(Compress *compression);
+
#endif
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 9d0056a569..f2390b7937 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -72,6 +72,25 @@ typedef struct _connParams
char *override_dbname;
} ConnParams;
+typedef enum
+{
+ COMPR_ALG_DEFAULT = -1,
+ COMPR_ALG_NONE,
+ COMPR_ALG_LIBZ,
+} CompressionAlgorithm;
+/* Should be called "method" or "library" ? */
+
+typedef struct Compress {
+ CompressionAlgorithm alg;
+ int level;
+ /* Is a nondefault level set ? This is useful since different compression
+ * methods have different "default" levels. For now we assume the levels
+ * are all integer, though.
+ */
+ bool level_set;
+} Compress;
+
+
typedef struct _restoreOptions
{
int createDB; /* Issue commands to create the database */
@@ -125,7 +144,7 @@ typedef struct _restoreOptions
int noDataForFailedTables;
int exit_on_error;
- int compression;
+ Compress compression;
int suppressDumpWarnings; /* Suppress output of WARNING entries
* to stderr */
bool single_txn;
@@ -281,7 +300,7 @@ extern Archive *OpenArchive(const char *FileSpec, const ArchiveFormat fmt);
/* Create a new archive */
extern Archive *CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker);
/* The --list option */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 1f82c6499b..3eb6c55600 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -70,7 +70,7 @@ typedef struct _parallelReadyList
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr);
static void _getObjectDescription(PQExpBuffer buf, TocEntry *te);
static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData);
@@ -98,7 +98,7 @@ static int _discoverArchiveFormat(ArchiveHandle *AH);
static int RestoringToDB(ArchiveHandle *AH);
static void dump_lo_buf(ArchiveHandle *AH);
static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
-static void SetOutput(ArchiveHandle *AH, const char *filename, int compression);
+static void SetOutput(ArchiveHandle *AH, const char *filename, Compress *compress);
static OutputContext SaveOutput(ArchiveHandle *AH);
static void RestoreOutput(ArchiveHandle *AH, OutputContext savedContext);
@@ -238,7 +238,7 @@ setupRestoreWorker(Archive *AHX)
/* Public */
Archive *
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupDumpWorker)
{
@@ -253,7 +253,9 @@ CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
Archive *
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
{
- ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, true, archModeRead, setupRestoreWorker);
+ Compress compress = {0};
+ ArchiveHandle *AH = _allocAH(FileSpec, fmt, &compress, true, archModeRead,
+ setupRestoreWorker);
return (Archive *) AH;
}
@@ -382,7 +384,7 @@ RestoreArchive(Archive *AHX)
* Make sure we won't need (de)compression we haven't got
*/
#ifndef HAVE_LIBZ
- if (AH->compression != 0 && AH->PrintTocDataPtr != NULL)
+ if (AH->compression.alg != COMPR_ALG_NONE && AH->PrintTocDataPtr != NULL)
{
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
@@ -457,8 +459,8 @@ RestoreArchive(Archive *AHX)
* Setup the output file if necessary.
*/
sav = SaveOutput(AH);
- if (ropt->filename || ropt->compression)
- SetOutput(AH, ropt->filename, ropt->compression);
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
+ SetOutput(AH, ropt->filename, &ropt->compression);
ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
@@ -738,7 +740,7 @@ RestoreArchive(Archive *AHX)
*/
AH->stage = STAGE_FINALIZING;
- if (ropt->filename || ropt->compression)
+ if (ropt->filename || ropt->compression.alg != COMPR_ALG_NONE)
RestoreOutput(AH, sav);
if (ropt->useDB)
@@ -1123,8 +1125,9 @@ PrintTOCSummary(Archive *AHX)
char stamp_str[64];
sav = SaveOutput(AH);
+ Assert(ropt->compression.alg == COMPR_ALG_NONE);
if (ropt->filename)
- SetOutput(AH, ropt->filename, 0 /* no compression */ );
+ SetOutput(AH, ropt->filename, &ropt->compression);
if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
localtime(&AH->createDate)) == 0)
@@ -1133,7 +1136,7 @@ PrintTOCSummary(Archive *AHX)
ahprintf(AH, ";\n; Archive created at %s\n", stamp_str);
ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %d\n",
sanitize_line(AH->archdbname, false),
- AH->tocCount, AH->compression);
+ AH->tocCount, AH->compression.alg);
switch (AH->format)
{
@@ -1487,7 +1490,7 @@ archprintf(Archive *AH, const char *fmt,...)
*******************************/
static void
-SetOutput(ArchiveHandle *AH, const char *filename, int compression)
+SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
{
int fn;
@@ -1510,12 +1513,12 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
/* If compression explicitly requested, use gzopen */
#ifdef HAVE_LIBZ
- if (compression != 0)
+ if (compression->alg != COMPR_ALG_NONE)
{
char fmode[14];
/* Don't use PG_BINARY_x since this is zlib */
- sprintf(fmode, "wb%d", compression);
+ sprintf(fmode, "wb%d", compression->level);
if (fn >= 0)
AH->OF = gzdopen(dup(fn), fmode);
else
@@ -2259,7 +2262,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
*/
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
- const int compression, bool dosync, ArchiveMode mode,
+ Compress *compression, bool dosync, ArchiveMode mode,
SetupWorkerPtrType setupWorkerPtr)
{
ArchiveHandle *AH;
@@ -2310,7 +2313,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
AH->toc->prev = AH->toc;
AH->mode = mode;
- AH->compression = compression;
+ AH->compression = *compression;
AH->dosync = dosync;
memset(&(AH->sqlparse), 0, sizeof(AH->sqlparse));
@@ -2325,7 +2328,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
* Force stdin/stdout into binary mode if that is what we are using.
*/
#ifdef WIN32
- if ((fmt != archNull || compression != 0) &&
+ if ((fmt != archNull || compression->alg != COMPR_ALG_NONE) &&
(AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0))
{
if (mode == archModeWrite)
@@ -3741,7 +3744,7 @@ WriteHead(ArchiveHandle *AH)
AH->WriteBytePtr(AH, AH->intSize);
AH->WriteBytePtr(AH, AH->offSize);
AH->WriteBytePtr(AH, AH->format);
- WriteInt(AH, AH->compression);
+ WriteInt(AH, AH->compression.alg);
crtm = *localtime(&AH->createDate);
WriteInt(AH, crtm.tm_sec);
WriteInt(AH, crtm.tm_min);
@@ -3816,15 +3819,15 @@ ReadHead(ArchiveHandle *AH)
if (AH->version >= K_VERS_1_2)
{
if (AH->version < K_VERS_1_4)
- AH->compression = AH->ReadBytePtr(AH);
+ AH->compression.alg = AH->ReadBytePtr(AH);
else
- AH->compression = ReadInt(AH);
+ AH->compression.alg = ReadInt(AH);
}
else
- AH->compression = Z_DEFAULT_COMPRESSION;
+ AH->compression.alg = Z_DEFAULT_COMPRESSION;
#ifndef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
pg_log_warning("archive is compressed, but this installation does not support compression -- no data will be available");
#endif
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a8ea5c7eae..6e033d040e 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -47,7 +47,8 @@
#define GZWRITE(p, s, n, fh) (fwrite(p, s, n, fh) * (s))
#define GZREAD(p, s, n, fh) fread(p, s, n, fh)
#define GZEOF(fh) feof(fh)
-/* this is just the redefinition of a libz constant */
+/* this is just the redefinition of a libz constant, in case zlib isn't
+ * available */
#define Z_DEFAULT_COMPRESSION (-1)
typedef struct _z_stream
@@ -329,14 +330,7 @@ struct _archiveHandle
DumpId *tableDataId; /* TABLE DATA ids, indexed by table dumpId */
struct _tocEntry *currToc; /* Used when dumping data */
- int compression; /*---------
- * Compression requested on open().
- * Possible values for compression:
- * -1 Z_DEFAULT_COMPRESSION
- * 0 COMPRESSION_NONE
- * 1-9 levels for gzip compression
- *---------
- */
+ Compress compression; /* Compression requested on open */
bool dosync; /* data requested to be synced on sight */
ArchiveMode mode; /* File mode - r or w */
void *formatData; /* Header data specific to file format */
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..55a887a236 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -298,7 +298,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
_WriteByte(AH, BLK_DATA); /* Block type */
WriteInt(AH, te->dumpId); /* For sanity check */
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -377,7 +377,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
WriteInt(AH, oid);
- ctx->cs = AllocateCompressor(AH->compression, _CustomWriteFunc);
+ ctx->cs = AllocateCompressor(&AH->compression, _CustomWriteFunc);
}
/*
@@ -566,7 +566,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
static void
_PrintData(ArchiveHandle *AH)
{
- ReadDataFromArchive(AH, AH->compression, _CustomReadFunc);
+ ReadDataFromArchive(AH, _CustomReadFunc);
}
static void
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 650b542fce..8bf869c6ca 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -202,7 +202,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
setFilePath(AH, fname, "toc.dat");
- tocFH = cfopen_read(fname, PG_BINARY_R);
+ tocFH = cfopen_read(fname, PG_BINARY_R, &AH->compression);
if (tocFH == NULL)
fatal("could not open input file \"%s\": %m", fname);
@@ -327,7 +327,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
setFilePath(AH, fname, tctx->filename);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -388,7 +388,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!filename)
return;
- cfp = cfopen_read(filename, PG_BINARY_R);
+ cfp = cfopen_read(filename, PG_BINARY_R, &AH->compression);
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
@@ -435,12 +435,13 @@ _LoadBlobs(ArchiveHandle *AH)
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
char line[MAXPGPATH];
+ Compress nocompression = {0};
StartRestoreBlobs(AH);
setFilePath(AH, fname, "blobs.toc");
- ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R);
+ ctx->blobsTocFH = cfopen_read(fname, PG_BINARY_R, &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open large object TOC file \"%s\" for input: %m",
@@ -573,6 +574,7 @@ _CloseArchive(ArchiveHandle *AH)
{
cfp *tocFH;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "toc.dat");
@@ -580,7 +582,7 @@ _CloseArchive(ArchiveHandle *AH)
ctx->pstate = ParallelBackupStart(AH);
/* The TOC is always created uncompressed */
- tocFH = cfopen_write(fname, PG_BINARY_W, 0);
+ tocFH = cfopen_write(fname, PG_BINARY_W, &nocompression);
if (tocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
ctx->dataFH = tocFH;
@@ -639,11 +641,12 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
{
lclContext *ctx = (lclContext *) AH->formatData;
char fname[MAXPGPATH];
+ Compress nocompression = {0};
setFilePath(AH, fname, "blobs.toc");
/* The blob TOC file is never compressed */
- ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
+ ctx->blobsTocFH = cfopen_write(fname, "ab", &nocompression);
if (ctx->blobsTocFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
}
@@ -661,7 +664,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
- ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
+ ctx->dataFH = cfopen_write(fname, PG_BINARY_W, &AH->compression);
if (ctx->dataFH == NULL)
fatal("could not open output file \"%s\": %m", fname);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 61c9c87a9f..4ba79ab924 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -39,6 +39,7 @@
#include "pg_backup_archiver.h"
#include "pg_backup_tar.h"
#include "pg_backup_utils.h"
+#include "compress_io.h"
#include "pgtar.h"
static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
@@ -196,10 +197,10 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
/*
* We don't support compression because reading the files back is not
- * possible since gzdopen uses buffered IO which totally screws file
+ * possible since gzdopen uses buffered IO which totally screws file XXX
* positioning.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
fatal("compression is not supported by tar archive format");
}
else
@@ -254,14 +255,8 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
if (te->dataDumper != NULL)
{
-#ifdef HAVE_LIBZ
- if (AH->compression == 0)
- sprintf(fn, "%d.dat", te->dumpId);
- else
- sprintf(fn, "%d.dat.gz", te->dumpId);
-#else
- sprintf(fn, "%d.dat", te->dumpId);
-#endif
+ const char *suffix = compress_suffix(&AH->compression);
+ sprintf(fn, "%d.dat%s", te->dumpId, suffix);
ctx->filename = pg_strdup(fn);
}
else
@@ -352,7 +347,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression == 0)
+ if (AH->compression.alg == COMPR_ALG_NONE)
tm->nFH = ctx->tarFH;
else
fatal("compression is not supported by tar archive format");
@@ -413,9 +408,9 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
#ifdef HAVE_LIBZ
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
{
- sprintf(fmode, "wb%d", AH->compression);
+ sprintf(fmode, "wb%d", AH->compression.level);
tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
if (tm->zFH == NULL)
fatal("could not open temporary file");
@@ -443,7 +438,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
/*
* Close the GZ file since we dup'd. This will flush the buffers.
*/
- if (AH->compression != 0)
+ if (AH->compression.alg != COMPR_ALG_NONE)
if (GZCLOSE(th->zFH) != 0)
fatal("could not close tar member");
@@ -868,7 +863,7 @@ _CloseArchive(ArchiveHandle *AH)
memcpy(ropt, AH->public.ropt, sizeof(RestoreOptions));
ropt->filename = NULL;
ropt->dropSchema = 1;
- ropt->compression = 0;
+ ropt->compression.alg = COMPR_ALG_NONE;
ropt->superuser = NULL;
ropt->suppressDumpWarnings = true;
@@ -952,16 +947,12 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
char fname[255];
- char *sfx;
+ const char *sfx;
if (oid == 0)
fatal("invalid OID for large object (%u)", oid);
- if (AH->compression != 0)
- sfx = ".gz";
- else
- sfx = "";
-
+ sfx = compress_suffix(&AH->compression);
sprintf(fname, "blob_%u.dat%s", oid, sfx);
tarPrintf(ctx->blobToc, "%u %s\n", oid, fname);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1ab98a2286..4cbc79aedc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -59,6 +59,7 @@
#include "getopt_long.h"
#include "libpq/libpq-fs.h"
#include "parallel.h"
+#include "compress_io.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "pg_dump.h"
@@ -297,6 +298,82 @@ static void setupDumpWorker(Archive *AHX);
static TableInfo *getRootTableInfo(TableInfo *tbinfo);
+/* Parse the string into compression options */
+static void
+parse_compression(const char *optarg, Compress *compress)
+{
+ if (optarg[0] == '0' && optarg[1] == '\0')
+ compress->alg = COMPR_ALG_NONE;
+ else if ((optarg[0] > '0' && optarg[0] <= '9') ||
+ optarg[0] == '-')
+ {
+ compress->alg = COMPR_ALG_LIBZ;
+ compress->level_set = true;
+ compress->level = atoi(optarg);
+ if (optarg[1] != '\0')
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+ }
+ else
+ {
+ /* Parse a more flexible string like level=3 alg=zlib opts=long */
+ for (;;)
+ {
+ char *eq = strchr(optarg, '=');
+ int len;
+
+ if (eq == NULL)
+ {
+ pg_log_error("compression options must be key=value: %s", optarg);
+ exit_nicely(1);
+ }
+
+ len = eq - optarg;
+ if (strncmp(optarg, "alg", len) == 0)
+ {
+ if (strchr(eq, ' '))
+ len = strchr(eq, ' ') - eq - 1;
+ else
+ len = strlen(eq) - len;
+ if (strncmp(1+eq, "zlib", len) == 0 ||
+ strncmp(1+eq, "libz", len) == 0)
+ compress->alg = COMPR_ALG_LIBZ;
+ else
+ {
+ pg_log_error("unknown compression algorithm: %s", 1+eq);
+ exit_nicely(1);
+ }
+ }
+ else if (strncmp(optarg, "level", len) == 0)
+ {
+ compress->level = atoi(1+eq);
+ compress->level_set = true;
+ }
+ else
+ {
+ pg_log_error("unknown compression setting: %s", optarg);
+ exit_nicely(1);
+ }
+
+ optarg = strchr(eq, ' ');
+ if (!optarg++)
+ break;
+ }
+
+ if (!compress->level_set)
+ {
+ const int default_compress_level[] = {
+ 0, /* COMPR_ALG_NONE */
+ Z_DEFAULT_COMPRESSION, /* COMPR_ALG_ZLIB */
+ };
+
+ compress->level = default_compress_level[compress->alg];
+ }
+ }
+}
+
int
main(int argc, char **argv)
{
@@ -319,7 +396,7 @@ main(int argc, char **argv)
char *use_role = NULL;
long rowsPerInsert;
int numWorkers = 1;
- int compressLevel = -1;
+ Compress compress = { .alg = COMPR_ALG_DEFAULT };
int plainText = 0;
ArchiveFormat archiveFormat = archUnknown;
ArchiveMode archiveMode;
@@ -532,12 +609,7 @@ main(int argc, char **argv)
break;
case 'Z': /* Compression Level */
- compressLevel = atoi(optarg);
- if (compressLevel < 0 || compressLevel > 9)
- {
- pg_log_error("compression level must be in range 0..9");
- exit_nicely(1);
- }
+ parse_compression(optarg, &compress);
break;
case 0:
@@ -679,20 +751,28 @@ main(int argc, char **argv)
plainText = 1;
/* Custom and directory formats are compressed by default, others not */
- if (compressLevel == -1)
+ if (compress.alg == COMPR_ALG_DEFAULT)
{
-#ifdef HAVE_LIBZ
if (archiveFormat == archCustom || archiveFormat == archDirectory)
- compressLevel = Z_DEFAULT_COMPRESSION;
- else
+ {
+#ifdef HAVE_LIBZ
+ compress.alg = COMPR_ALG_LIBZ;
+ compress.level = Z_DEFAULT_COMPRESSION;
#endif
- compressLevel = 0;
+ }
+ else
+ {
+ compress.alg = COMPR_ALG_NONE;
+ compress.level = 0;
+ }
}
#ifndef HAVE_LIBZ
- if (compressLevel != 0)
+ if (compress.alg == COMPR_ALG_LIBZ)
+ {
pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
- compressLevel = 0;
+ compress.alg = 0;
+ }
#endif
/*
@@ -723,7 +803,7 @@ main(int argc, char **argv)
fatal("option --index-collation-versions-unknown only works in binary upgrade mode");
/* Open the output file */
- fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
+ fout = CreateArchive(filename, archiveFormat, &compress, dosync,
archiveMode, setupDumpWorker);
/* Make dump options accessible right away */
@@ -957,10 +1037,7 @@ main(int argc, char **argv)
ropt->sequence_data = dopt.sequence_data;
ropt->binary_upgrade = dopt.binary_upgrade;
- if (compressLevel == -1)
- ropt->compression = 0;
- else
- ropt->compression = compressLevel;
+ ropt->compression = compress;
ropt->suppressDumpWarnings = true; /* We've already shown them */
--
2.17.0
0004-struct-compressLibs.patchtext/x-diff; charset=us-asciiDownload
From b26d3fa15723ab38057276471942e79cf4c7789b Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Thu, 24 Dec 2020 22:08:43 -0600
Subject: [PATCH 04/20] struct compressLibs
---
src/bin/pg_dump/compress_io.c | 55 +++++++++++++++++++--------
src/bin/pg_dump/compress_io.h | 7 ++++
src/bin/pg_dump/pg_backup_directory.c | 20 ++++++----
src/bin/pg_dump/pg_dump.c | 25 +++++++-----
4 files changed, 75 insertions(+), 32 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index db16fd33f2..21957d68f3 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -56,6 +56,18 @@
#include "compress_io.h"
#include "pg_backup_utils.h"
+const struct compressLibs
+compresslibs[] = {
+ { COMPR_ALG_NONE, "no", "", 0, },
+ { COMPR_ALG_NONE, "none", "", 0, }, /* Alternate name */
+
+// #ifdef HAVE_LIBZ?
+ { COMPR_ALG_LIBZ, "libz", ".gz", Z_DEFAULT_COMPRESSION },
+ { COMPR_ALG_LIBZ, "zlib", ".gz", Z_DEFAULT_COMPRESSION }, /* Alternate name */
+
+ { 0, NULL, } /* sentinel */
+};
+
/*----------------------
* Compressor API
*----------------------
@@ -401,7 +413,7 @@ struct cfp
#endif
};
-static int hasSuffix(const char *filename, const char *suffix);
+static int hasSuffix(const char *filename);
/* free() without changing errno; useful in several places below */
static void
@@ -428,7 +440,7 @@ cfopen_read(const char *path, const char *mode, Compress *compression)
{
cfp *fp;
- if (hasSuffix(path, ".gz"))
+ if (hasSuffix(path))
fp = cfopen(path, mode, compression);
else
{
@@ -656,18 +668,29 @@ get_cfp_error(cfp *fp)
return strerror(errno);
}
+/* Return true iff the filename has a known compression suffix */
static int
-hasSuffix(const char *filename, const char *suffix)
+hasSuffix(const char *filename)
{
- int filenamelen = strlen(filename);
- int suffixlen = strlen(suffix);
+ for (int i = 0; compresslibs[i].name != NULL; ++i)
+ {
+ const char *suffix = compresslibs[i].suffix;
+ int filenamelen = strlen(filename);
+ int suffixlen = strlen(suffix);
- if (filenamelen < suffixlen)
- return 0;
+ /* COMPR_ALG_NONE has an empty "suffix", which doesn't count */
+ if (suffixlen == 0)
+ continue;
+
+ if (filenamelen < suffixlen)
+ continue;
- return memcmp(&filename[filenamelen - suffixlen],
- suffix,
- suffixlen) == 0;
+ if (memcmp(&filename[filenamelen - suffixlen],
+ suffix, suffixlen) == 0)
+ return true;
+ }
+
+ return false;
}
/*
@@ -677,11 +700,13 @@ hasSuffix(const char *filename, const char *suffix)
const char *
compress_suffix(Compress *compression)
{
- switch (compression->alg)
+ for (int i = 0; compresslibs[i].name != NULL; ++i)
{
- case COMPR_ALG_LIBZ:
- return ".gz";
- default:
- return "";
+ if (compression->alg != compresslibs[i].alg)
+ continue;
+
+ return compresslibs[i].suffix;
}
+
+ return "";
}
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index 2c073676eb..fb9d659acc 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -47,6 +47,13 @@ extern void WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
const void *data, size_t dLen);
extern void EndCompressor(ArchiveHandle *AH, CompressorState *cs);
+struct compressLibs {
+ const CompressionAlgorithm alg;
+ const char *name; /* Name in -Z alg= */
+ const char *suffix; /* file extension */
+ const int defaultlevel; /* Default compression level */
+};
+extern const struct compressLibs compresslibs[];
typedef struct cfp cfp;
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 8bf869c6ca..75c1bf22e4 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -768,14 +768,20 @@ _PrepParallelRestore(ArchiveHandle *AH)
*/
setFilePath(AH, fname, tctx->filename);
- if (stat(fname, &st) == 0)
- te->dataLength = st.st_size;
- else
+ for (int i = 0; compresslibs[i].name != NULL; ++i)
{
- /* It might be compressed */
- strlcat(fname, ".gz", sizeof(fname));
- if (stat(fname, &st) == 0)
- te->dataLength = st.st_size;
+ char filename[MAXPGPATH];
+ int ret;
+
+ snprintf(filename, sizeof(filename), "%s%s", fname,
+ compresslibs[i].suffix);
+
+ ret = stat(fname, &st);
+ if (ret < 0) // && errno == ENOENT)
+ continue;
+
+ te->dataLength = st.st_size;
+ break;
}
/*
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4cbc79aedc..75985fd4d3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -318,7 +318,7 @@ parse_compression(const char *optarg, Compress *compress)
}
else
{
- /* Parse a more flexible string like level=3 alg=zlib opts=long */
+ /* Parse a more flexible string like -Z level=3 -Z alg=zlib -Z checksum=1 */
for (;;)
{
char *eq = strchr(optarg, '=');
@@ -333,14 +333,19 @@ parse_compression(const char *optarg, Compress *compress)
len = eq - optarg;
if (strncmp(optarg, "alg", len) == 0)
{
- if (strchr(eq, ' '))
- len = strchr(eq, ' ') - eq - 1;
- else
- len = strlen(eq) - len;
- if (strncmp(1+eq, "zlib", len) == 0 ||
- strncmp(1+eq, "libz", len) == 0)
- compress->alg = COMPR_ALG_LIBZ;
- else
+ len = strlen(eq) - len;
+
+ for (int i = 0; compresslibs[i].name != NULL; ++i)
+ {
+ if (strlen(1+eq) != strlen(compresslibs[i].name))
+ continue;
+ if (strncmp(1+eq, compresslibs[i].name, len) != 0)
+ continue;
+ compress->alg = compresslibs[i].alg;
+ break;
+ }
+
+ if (compress->alg == COMPR_ALG_DEFAULT)
{
pg_log_error("unknown compression algorithm: %s", 1+eq);
exit_nicely(1);
@@ -363,7 +368,7 @@ parse_compression(const char *optarg, Compress *compress)
}
if (!compress->level_set)
- {
+ { // XXX
const int default_compress_level[] = {
0, /* COMPR_ALG_NONE */
Z_DEFAULT_COMPRESSION, /* COMPR_ALG_ZLIB */
--
2.17.0
0005-Use-cf-abstraction-in-archiver-and-tar.patchtext/x-diff; charset=us-asciiDownload
From 1bac5d02bbcc9c34dcb44b358bc27f3d204bb584 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 25 Dec 2020 00:34:01 -0600
Subject: [PATCH 05/20] Use cf* abstraction in archiver and tar
..rather than direct, conditional calls to gzopen/fopen.
See also: bf9aa490db24b2334b3595ee33653bf2fe39208c
---
src/bin/pg_dump/compress_io.c | 53 +++++++++++++
src/bin/pg_dump/compress_io.h | 5 ++
src/bin/pg_dump/pg_backup_archiver.c | 109 +++++++++------------------
src/bin/pg_dump/pg_backup_archiver.h | 16 ++--
src/bin/pg_dump/pg_backup_tar.c | 85 +++++----------------
5 files changed, 117 insertions(+), 151 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 21957d68f3..d66d6f60f5 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -540,6 +540,58 @@ cfopen(const char *path, const char *mode, Compress *compression)
}
}
+/*
+ * Open a file descriptor, with specified compression.
+ * Returns an opaque cfp object.
+ */
+cfp *
+cfdopen(int fd, const char *mode, Compress *compression)
+{
+ cfp *fp = pg_malloc0(sizeof(cfp));
+
+ switch (compression->alg)
+ {
+#ifdef HAVE_LIBZ
+ case COMPR_ALG_LIBZ:
+ if (compression->level != Z_DEFAULT_COMPRESSION)
+ {
+ /* user has specified a compression level, so tell zlib to use it */
+ char mode_compression[32];
+
+ snprintf(mode_compression, sizeof(mode_compression), "%s%d",
+ mode, compression->level);
+ fp->compressedfp = gzdopen(fd, mode_compression);
+ }
+ else
+ {
+ /* don't specify a level, just use the zlib default */
+ fp->compressedfp = gzdopen(fd, mode);
+ }
+
+ if (fp->compressedfp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ return fp;
+#endif
+
+ case COMPR_ALG_NONE:
+ fp->uncompressedfp = fdopen(fd, mode);
+ if (fp->uncompressedfp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ else
+ setvbuf(fp->uncompressedfp, NULL, _IONBF, 0);
+ return fp;
+
+ default:
+ /* Should not happen */
+ fatal("requested compression not available in this installation");
+ }
+}
int
cfread(void *ptr, int size, cfp *fp)
@@ -616,6 +668,7 @@ cfgets(cfp *fp, char *buf, int len)
return fgets(buf, len, fp->uncompressedfp);
}
+/* Close the given compressed or uncompressed stream; return 0 on success. */
int
cfclose(cfp *fp)
{
diff --git a/src/bin/pg_dump/compress_io.h b/src/bin/pg_dump/compress_io.h
index fb9d659acc..318a6b5340 100644
--- a/src/bin/pg_dump/compress_io.h
+++ b/src/bin/pg_dump/compress_io.h
@@ -21,6 +21,10 @@
#define ZLIB_OUT_SIZE 4096
#define ZLIB_IN_SIZE 4096
+/* Forward declaration */
+struct ArchiveHandle;
+typedef struct _archiveHandle ArchiveHandle;
+
/* Prototype for callback function to WriteDataToArchive() */
typedef void (*WriteFunc) (ArchiveHandle *AH, const char *buf, size_t len);
@@ -58,6 +62,7 @@ extern const struct compressLibs compresslibs[];
typedef struct cfp cfp;
extern cfp *cfopen(const char *path, const char *mode, Compress *compression);
+extern cfp *cfdopen(int fd, const char *mode, Compress *compression);
extern cfp *cfopen_read(const char *path, const char *mode, Compress *compression);
extern cfp *cfopen_write(const char *path, const char *mode, Compress *compression);
extern int cfread(void *ptr, int size, cfp *fp);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 3eb6c55600..bd06fbb787 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -39,17 +39,11 @@
#include "pg_backup_archiver.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
+#include "compress_io.h"
#define TEXT_DUMP_HEADER "--\n-- PostgreSQL database dump\n--\n\n"
#define TEXT_DUMPALL_HEADER "--\n-- PostgreSQL database cluster dump\n--\n\n"
-/* state needed to save/restore an archive's output target */
-typedef struct _outputContext
-{
- void *OF;
- int gzOut;
-} OutputContext;
-
/*
* State for tracking TocEntrys that are ready to process during a parallel
* restore. (This used to be a list, and we still call it that, though now
@@ -99,8 +93,8 @@ static int RestoringToDB(ArchiveHandle *AH);
static void dump_lo_buf(ArchiveHandle *AH);
static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
static void SetOutput(ArchiveHandle *AH, const char *filename, Compress *compress);
-static OutputContext SaveOutput(ArchiveHandle *AH);
-static void RestoreOutput(ArchiveHandle *AH, OutputContext savedContext);
+static cfp *SaveOutput(ArchiveHandle *AH);
+static void RestoreOutput(ArchiveHandle *AH, cfp *fp);
static int restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel);
static void restore_toc_entries_prefork(ArchiveHandle *AH,
@@ -270,10 +264,8 @@ CloseArchive(Archive *AHX)
AH->ClosePtr(AH);
/* Close the output */
- if (AH->gzOut)
- res = GZCLOSE(AH->OF);
- else if (AH->OF != stdout)
- res = fclose(AH->OF);
+ if ((FILE *)AH->OF != stdout)
+ res = cfclose(AH->OF);
if (res != 0)
fatal("could not close output file: %m");
@@ -355,7 +347,7 @@ RestoreArchive(Archive *AHX)
RestoreOptions *ropt = AH->public.ropt;
bool parallel_mode;
TocEntry *te;
- OutputContext sav;
+ cfp *sav;
AH->stage = STAGE_INITIALIZING;
@@ -1120,7 +1112,7 @@ PrintTOCSummary(Archive *AHX)
RestoreOptions *ropt = AH->public.ropt;
TocEntry *te;
teSection curSection;
- OutputContext sav;
+ cfp *sav;
const char *fmtName;
char stamp_str[64];
@@ -1492,6 +1484,7 @@ archprintf(Archive *AH, const char *fmt,...)
static void
SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
{
+ char fmode[14];
int fn;
if (filename)
@@ -1511,38 +1504,22 @@ SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
else
fn = fileno(stdout);
- /* If compression explicitly requested, use gzopen */
-#ifdef HAVE_LIBZ
- if (compression->alg != COMPR_ALG_NONE)
+ if (fn >= 0)
{
- char fmode[14];
+ /* Handle output to stdout */
+ sprintf(fmode, "%sb%d",
+ AH->mode == archModeAppend ? PG_BINARY_A : PG_BINARY_W,
+ compression->level);
- /* Don't use PG_BINARY_x since this is zlib */
- sprintf(fmode, "wb%d", compression->level);
- if (fn >= 0)
- AH->OF = gzdopen(dup(fn), fmode);
- else
- AH->OF = gzopen(filename, fmode);
- AH->gzOut = 1;
+ AH->OF = cfdopen(dup(fn), fmode, compression);
}
else
-#endif
- { /* Use fopen */
- if (AH->mode == archModeAppend)
- {
- if (fn >= 0)
- AH->OF = fdopen(dup(fn), PG_BINARY_A);
- else
- AH->OF = fopen(filename, PG_BINARY_A);
- }
- else
- {
- if (fn >= 0)
- AH->OF = fdopen(dup(fn), PG_BINARY_W);
- else
- AH->OF = fopen(filename, PG_BINARY_W);
- }
- AH->gzOut = 0;
+ {
+ Assert(filename != NULL);
+ sprintf(fmode, "%cb%d",
+ AH->mode == archModeAppend ? 'a' : 'w',
+ compression->level);
+ AH->OF = cfopen(filename, fmode, compression);
}
if (!AH->OF)
@@ -1554,32 +1531,22 @@ SetOutput(ArchiveHandle *AH, const char *filename, Compress *compression)
}
}
-static OutputContext
+/* Return a pointer to the old FP */
+static cfp *
SaveOutput(ArchiveHandle *AH)
{
- OutputContext sav;
-
- sav.OF = AH->OF;
- sav.gzOut = AH->gzOut;
-
- return sav;
+ return AH->OF;
}
static void
-RestoreOutput(ArchiveHandle *AH, OutputContext savedContext)
+RestoreOutput(ArchiveHandle *AH, cfp *savedContext)
{
int res;
-
- if (AH->gzOut)
- res = GZCLOSE(AH->OF);
- else
- res = fclose(AH->OF);
-
+ res = cfclose(AH->OF);
if (res != 0)
fatal("could not close output file: %m");
- AH->gzOut = savedContext.gzOut;
- AH->OF = savedContext.OF;
+ AH->OF = savedContext;
}
@@ -1703,22 +1670,14 @@ ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
bytes_written = size * nmemb;
}
- else if (AH->gzOut)
- bytes_written = GZWRITE(ptr, size, nmemb, AH->OF);
else if (AH->CustomOutPtr)
bytes_written = AH->CustomOutPtr(AH, ptr, size * nmemb);
-
+ else if (RestoringToDB(AH))
+ /* If we're doing a restore, and it's direct to DB, and we're
+ * connected then send it to the DB. */
+ bytes_written = ExecuteSqlCommandBuf(&AH->public, (const char *) ptr, size * nmemb);
else
- {
- /*
- * If we're doing a restore, and it's direct to DB, and we're
- * connected then send it to the DB.
- */
- if (RestoringToDB(AH))
- bytes_written = ExecuteSqlCommandBuf(&AH->public, (const char *) ptr, size * nmemb);
- else
- bytes_written = fwrite(ptr, size, nmemb, AH->OF) * size;
- }
+ bytes_written = cfwrite(ptr, size * nmemb, AH->OF);
if (bytes_written != size * nmemb)
WRITE_ERROR_EXIT;
@@ -2127,6 +2086,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
fh = stdin;
if (!fh)
fatal("could not open input file: %m");
+ setvbuf(fh, NULL, _IONBF, 0);
}
if ((cnt = fread(sig, 1, 5, fh)) != 5)
@@ -2266,6 +2226,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
SetupWorkerPtrType setupWorkerPtr)
{
ArchiveHandle *AH;
+ Compress nocompression = {0};
pg_log_debug("allocating AH for %s, format %d",
FileSpec ? FileSpec : "(stdio)", fmt);
@@ -2319,8 +2280,8 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
memset(&(AH->sqlparse), 0, sizeof(AH->sqlparse));
/* Open stdout with no compression for AH output handle */
- AH->gzOut = 0;
- AH->OF = stdout;
+ AH->OF = cfdopen(fileno(stdout), "w", &nocompression);
+ // AH->OF = cfdopen(STDOUT_FILENO, "w", compression); // XXX
/*
* On Windows, we need to use binary mode to read/write non-text files,
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 6e033d040e..9f511b49b9 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -30,6 +30,9 @@
#include "pg_backup.h"
#include "pqexpbuffer.h"
+/* Forward declaration XXX: CIRCULAR */
+typedef struct cfp cfp;
+
#define LOBBUFSIZE 16384
/*
@@ -38,19 +41,11 @@
*/
#ifdef HAVE_LIBZ
#include <zlib.h>
-#define GZCLOSE(fh) gzclose(fh)
-#define GZWRITE(p, s, n, fh) gzwrite(fh, p, (n) * (s))
-#define GZREAD(p, s, n, fh) gzread(fh, p, (n) * (s))
-#define GZEOF(fh) gzeof(fh)
#else
-#define GZCLOSE(fh) fclose(fh)
-#define GZWRITE(p, s, n, fh) (fwrite(p, s, n, fh) * (s))
-#define GZREAD(p, s, n, fh) fread(p, s, n, fh)
-#define GZEOF(fh) feof(fh)
+
/* this is just the redefinition of a libz constant, in case zlib isn't
* available */
#define Z_DEFAULT_COMPRESSION (-1)
-
typedef struct _z_stream
{
void *next_in;
@@ -318,8 +313,7 @@ struct _archiveHandle
char *fSpec; /* Archive File Spec */
FILE *FH; /* General purpose file handle */
- void *OF;
- int gzOut; /* Output file */
+ cfp *OF; /* Output file (compressed or not) */
struct _tocEntry *toc; /* Header of circular list of TOC entries */
int tocCount; /* Number of TOC entries */
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 4ba79ab924..16f4e0792a 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -66,12 +66,7 @@ static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
typedef struct
{
-#ifdef HAVE_LIBZ
- gzFile zFH;
-#else
- FILE *zFH;
-#endif
- FILE *nFH;
+ cfp *FH;
FILE *tarFH;
FILE *tmpFH;
char *targetFile;
@@ -191,7 +186,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
* Make unbuffered since we will dup() it, and the buffers screw each
* other
*/
- /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
+ // setvbuf(ctx->tarFH, NULL, _IONBF, 0);
ctx->hasSeek = checkSeek(ctx->tarFH);
@@ -223,7 +218,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
* Make unbuffered since we will dup() it, and the buffers screw each
* other
*/
- /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
+ setvbuf(ctx->tarFH, NULL, _IONBF, 0);
ctx->tarFHpos = 0;
@@ -321,10 +316,6 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
lclContext *ctx = (lclContext *) AH->formatData;
TAR_MEMBER *tm;
-#ifdef HAVE_LIBZ
- char fmode[14];
-#endif
-
if (mode == 'r')
{
tm = _tarPositionTo(AH, filename);
@@ -345,16 +336,10 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
}
}
-#ifdef HAVE_LIBZ
-
if (AH->compression.alg == COMPR_ALG_NONE)
- tm->nFH = ctx->tarFH;
+ tm->FH = cfdopen(dup(fileno(ctx->tarFH)), "rb", &AH->compression);
else
fatal("compression is not supported by tar archive format");
- /* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
-#else
- tm->nFH = ctx->tarFH;
-#endif
}
else
{
@@ -406,21 +391,11 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
umask(old_umask);
-#ifdef HAVE_LIBZ
-
- if (AH->compression.alg != COMPR_ALG_NONE)
- {
- sprintf(fmode, "wb%d", AH->compression.level);
- tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
- if (tm->zFH == NULL)
- fatal("could not open temporary file");
- }
- else
- tm->nFH = tm->tmpFH;
-#else
-
- tm->nFH = tm->tmpFH;
-#endif
+ tm->FH = cfdopen(dup(fileno(tm->tmpFH)),
+ mode == 'r' ? "r" : "w",
+ &AH->compression);
+ if (tm->FH == NULL)
+ fatal("could not open temporary file");
tm->AH = AH;
tm->targetFile = pg_strdup(filename);
@@ -435,12 +410,14 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
static void
tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
{
+ int res;
+
/*
* Close the GZ file since we dup'd. This will flush the buffers.
*/
- if (AH->compression.alg != COMPR_ALG_NONE)
- if (GZCLOSE(th->zFH) != 0)
- fatal("could not close tar member");
+ res = cfclose(th->FH);
+ if (res != 0)
+ fatal("could not close tar member");
if (th->mode == 'w')
_tarAddFile(AH, th); /* This will close the temp file */
@@ -453,8 +430,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
if (th->targetFile)
free(th->targetFile);
- th->nFH = NULL;
- th->zFH = NULL;
+ th->FH = NULL;
}
#ifdef __NOT_USED__
@@ -540,29 +516,9 @@ _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
}
else if (th)
{
- if (th->zFH)
- {
- res = GZREAD(&((char *) buf)[used], 1, len, th->zFH);
- if (res != len && !GZEOF(th->zFH))
- {
-#ifdef HAVE_LIBZ
- int errnum;
- const char *errmsg = gzerror(th->zFH, &errnum);
-
- fatal("could not read from input file: %s",
- errnum == Z_ERRNO ? strerror(errno) : errmsg);
-#else
- fatal("could not read from input file: %s",
- strerror(errno));
-#endif
- }
- }
- else
- {
- res = fread(&((char *) buf)[used], 1, len, th->nFH);
- if (res != len && !feof(th->nFH))
- READ_ERROR_EXIT(th->nFH);
- }
+ res = cfread(&((char *) buf)[used], len, th->FH);
+ if (res != len && !cfeof(th->FH))
+ fatal("could not read from input file: %m");
}
}
@@ -594,10 +550,7 @@ tarWrite(const void *buf, size_t len, TAR_MEMBER *th)
{
size_t res;
- if (th->zFH != NULL)
- res = GZWRITE(buf, 1, len, th->zFH);
- else
- res = fwrite(buf, 1, len, th->nFH);
+ res = cfwrite(buf, len, th->FH);
th->pos += res;
return res;
--
2.17.0
0006-pg_dump-zstd-compression.patchtext/x-diff; charset=us-asciiDownload
From b8eb13b5bd4114a9860cfa83f8240ab09db588b4 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Tue, 22 Dec 2020 01:06:26 -0600
Subject: [PATCH 06/20] pg_dump: zstd compression
document any change in search for .gz?
docs
Maybe compress_io should be split so all the library-specific stuff are in
separate files, like compress_{zlib/zstd}.c
---
configure | 123 ++++++-
configure.ac | 22 ++
src/bin/pg_dump/compress_io.c | 480 ++++++++++++++++++++++++++
src/bin/pg_dump/pg_backup.h | 14 +
src/bin/pg_dump/pg_backup_archiver.h | 4 +
src/bin/pg_dump/pg_backup_directory.c | 8 +-
src/bin/pg_dump/pg_dump.c | 39 +++
src/include/pg_config.h.in | 3 +
src/tools/msvc/Solution.pm | 1 +
9 files changed, 686 insertions(+), 8 deletions(-)
diff --git a/configure b/configure
index 11a4284e5b..fe739879af 100755
--- a/configure
+++ b/configure
@@ -698,6 +698,7 @@ with_gnu_ld
LD
LDFLAGS_SL
LDFLAGS_EX
+with_zstd
with_zlib
with_system_tzdata
with_libxslt
@@ -798,6 +799,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -866,6 +868,7 @@ with_libxml
with_libxslt
with_system_tzdata
with_zlib
+with_zstd
with_gnu_ld
enable_largefile
'
@@ -935,6 +938,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1187,6 +1191,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1324,7 +1337,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1477,6 +1490,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1570,6 +1584,7 @@ Optional Packages:
--with-system-tzdata=DIR
use system time zone data in DIR
--without-zlib do not use Zlib
+ --with-zstd use Zstd compression library
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
Some influential environment variables:
@@ -8601,6 +8616,35 @@ fi
+#
+# Zstd
+#
+
+
+
+# Check whether --with-zstd was given.
+if test "${with_zstd+set}" = set; then :
+ withval=$with_zstd;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ as_fn_error $? "no argument expected for --with-zstd option" "$LINENO" 5
+ ;;
+ esac
+
+else
+ with_zstd=no
+
+fi
+
+
+
+
#
# Assignments
#
@@ -12092,6 +12136,59 @@ fi
fi
+if test "$with_zstd" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZSTD_compressStream2 in -lzstd" >&5
+$as_echo_n "checking for ZSTD_compressStream2 in -lzstd... " >&6; }
+if ${ac_cv_lib_zstd_ZSTD_compressStream2+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzstd $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ZSTD_compressStream2 ();
+int
+main ()
+{
+return ZSTD_compressStream2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_zstd_ZSTD_compressStream2=yes
+else
+ ac_cv_lib_zstd_ZSTD_compressStream2=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_zstd_ZSTD_compressStream2" >&5
+$as_echo "$ac_cv_lib_zstd_ZSTD_compressStream2" >&6; }
+if test "x$ac_cv_lib_zstd_ZSTD_compressStream2" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZSTD 1
+_ACEOF
+
+ LIBS="-lzstd $LIBS"
+
+else
+ as_fn_error $? "zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+fi
+
if test "$enable_spinlocks" = yes; then
$as_echo "#define HAVE_SPINLOCKS 1" >>confdefs.h
@@ -13295,6 +13392,20 @@ Use --without-zlib to disable zlib support." "$LINENO" 5
fi
+fi
+
+if test "$with_zstd" = yes; then
+ ac_fn_c_check_header_mongrel "$LINENO" "zstd.h" "ac_cv_header_zstd_h" "$ac_includes_default"
+if test "x$ac_cv_header_zstd_h" = xyes; then :
+
+else
+ as_fn_error $? "zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support." "$LINENO" 5
+fi
+
+
fi
if test "$with_gssapi" = yes ; then
@@ -14689,7 +14800,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14735,7 +14846,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14759,7 +14870,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14804,7 +14915,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -14828,7 +14939,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
diff --git a/configure.ac b/configure.ac
index fc523c6aeb..744836ea7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -999,6 +999,13 @@ PGAC_ARG_BOOL(with, zlib, yes,
[do not use Zlib])
AC_SUBST(with_zlib)
+#
+# Zstd
+#
+PGAC_ARG_BOOL(with, zstd, no,
+ [use Zstd compression library])
+AC_SUBST(with_zstd)
+
#
# Assignments
#
@@ -1186,6 +1193,14 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_LIB(zstd, ZSTD_compressStream2, [],
+ [AC_MSG_ERROR([zstd library not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$enable_spinlocks" = yes; then
AC_DEFINE(HAVE_SPINLOCKS, 1, [Define to 1 if you have spinlocks.])
else
@@ -1400,6 +1415,13 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
+if test "$with_zstd" = yes; then
+ AC_CHECK_HEADER(zstd.h, [], [AC_MSG_ERROR([zstd header not found
+If you have zstd already installed, see config.log for details on the
+failure. It is possible the compiler isn't looking in the proper directory.
+Use --without-zstd to disable zstd support.])])
+fi
+
if test "$with_gssapi" = yes ; then
AC_CHECK_HEADERS(gssapi/gssapi.h, [],
[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index d66d6f60f5..285f554c1a 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -65,6 +65,18 @@ compresslibs[] = {
{ COMPR_ALG_LIBZ, "libz", ".gz", Z_DEFAULT_COMPRESSION },
{ COMPR_ALG_LIBZ, "zlib", ".gz", Z_DEFAULT_COMPRESSION }, /* Alternate name */
+#ifdef HAVE_LIBZSTD
+ /*
+ * ZSTD doesen't have a #define for it, but 0 means "the current default".
+ * Note that ZSTD_CLEVEL_DEFAULT is currently defined to 3.
+ *
+ * Block size should be ZSTD_DStreamOutSize(), but needs to be
+ * constant, so use ZSTD_BLOCKSIZE_MAX (128kB)
+ */
+ { COMPR_ALG_ZSTD, "zst", ".zst", 0 },
+ { COMPR_ALG_ZSTD, "zstd", ".zst", 0 }, /* Alternate name */
+#endif /* HAVE_LIBZSTD */
+
{ 0, NULL, } /* sentinel */
};
@@ -84,6 +96,18 @@ struct CompressorState
char *zlibOut;
size_t zlibOutSize;
#endif
+
+#ifdef HAVE_LIBZSTD
+ union {
+ struct {
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ } zstd;
+ } u;
+#endif
+
};
/* Routines that support zlib compressed data I/O */
@@ -97,6 +121,15 @@ static void WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
static void EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs);
#endif
+#ifdef HAVE_LIBZSTD
+static ZSTD_CStream *ZstdCStreamParams(Compress *compress);
+static void InitCompressorZstd(CompressorState *cs, Compress *compress);
+static void EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs);
+static void WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen);
+static void ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF);
+#endif
+
/* Routines that support uncompressed data I/O */
static void ReadDataFromArchiveNone(ArchiveHandle *AH, ReadFunc readF);
static void WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
@@ -125,6 +158,13 @@ AllocateCompressor(Compress *compression, WriteFunc writeF)
InitCompressorZlib(cs, compression);
break;
#endif
+
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ InitCompressorZstd(cs, compression);
+ break;
+#endif
+
case COMPR_ALG_NONE:
/* Do nothing */
break;
@@ -153,6 +193,13 @@ ReadDataFromArchive(ArchiveHandle *AH, ReadFunc readF)
ReadDataFromArchiveZlib(AH, readF);
break;
#endif
+
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ ReadDataFromArchiveZstd(AH, readF);
+ break;
+#endif
+
default:
/* Should not happen */
fatal("requested compression not available in this installation");
@@ -173,6 +220,12 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
WriteDataToArchiveZlib(AH, cs, data, dLen);
break;
#endif
+
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ WriteDataToArchiveZstd(AH, cs, data, dLen);
+ break;
+#endif
case COMPR_ALG_NONE:
WriteDataToArchiveNone(AH, cs, data, dLen);
break;
@@ -193,11 +246,202 @@ EndCompressor(ArchiveHandle *AH, CompressorState *cs)
if (cs->comprAlg == COMPR_ALG_LIBZ)
EndCompressorZlib(AH, cs);
#endif
+
+#ifdef HAVE_LIBZSTD
+ if (cs->comprAlg == COMPR_ALG_ZSTD)
+ EndCompressorZstd(AH, cs);
+#endif
+
free(cs);
}
/* Private routines, specific to each compression method. */
+#ifdef HAVE_LIBZSTD
+
+static void ZSTD_CCtx_setParam_or_die(ZSTD_CStream *cstream,
+ ZSTD_cParameter param, int value)
+
+{
+ size_t res;
+ res = ZSTD_CCtx_setParameter(cstream, param, value);
+ if (ZSTD_isError(res))
+ fatal("could not set compression parameter: %s",
+ ZSTD_getErrorName(res));
+}
+
+/* Return a compression stream with parameters set per argument */
+static ZSTD_CStream*
+ZstdCStreamParams(Compress *compress)
+{
+ ZSTD_CStream *cstream;
+ cstream = ZSTD_createCStream();
+ if (cstream == NULL)
+ fatal("could not initialize compression library");
+
+ if (compress->level != 0) // XXX: ZSTD_CLEVEL_DEFAULT
+ {
+ size_t res;
+ res = ZSTD_CCtx_setParameter(cstream,
+ ZSTD_c_compressionLevel, compress->level);
+ if (ZSTD_isError(res))
+ fatal("could not set compression level: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ if (compress->zstd.longdistance) // XXX: ternary
+ ZSTD_CCtx_setParam_or_die(cstream,
+ ZSTD_c_enableLongDistanceMatching,
+ compress->zstd.longdistance);
+
+ if (compress->zstd.checksum)
+ ZSTD_CCtx_setParam_or_die(cstream, ZSTD_c_checksumFlag,
+ compress->zstd.checksum);
+
+// not supported in my library ?
+ if (compress->zstd.threads)
+ ZSTD_CCtx_setParam_or_die(cstream, ZSTD_c_nbWorkers,
+ compress->zstd.threads);
+
+#if 0
+ /* Still marked as experimental */
+ if (compress->zstd.rsyncable)
+ ZSTD_CCtx_setParam_or_die(cstream, ZSTD_c_rsyncable, 1);
+#endif
+
+ return cstream;
+}
+
+static void
+InitCompressorZstd(CompressorState *cs, Compress *compress)
+{
+ cs->u.zstd.cstream = ZstdCStreamParams(compress);
+ /* XXX: initialize safely like the corresponding zlib "paranoia" */
+ cs->u.zstd.output.size = ZSTD_CStreamOutSize();
+ cs->u.zstd.output.dst = pg_malloc(cs->u.zstd.output.size);
+ cs->u.zstd.output.pos = 0;
+}
+
+static void
+EndCompressorZstd(ArchiveHandle *AH, CompressorState *cs)
+{
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ for (;;)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ &cs->u.zstd.input, ZSTD_e_end);
+
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ if (res == 0)
+ break;
+
+ if (ZSTD_isError(res))
+ fatal("could not close compression stream: %s",
+ ZSTD_getErrorName(res));
+ }
+
+ // XXX: retval
+ ZSTD_freeCStream(cs->u.zstd.cstream);
+}
+
+static void
+WriteDataToArchiveZstd(ArchiveHandle *AH, CompressorState *cs,
+ const char *data, size_t dLen)
+{
+ ZSTD_inBuffer *input = &cs->u.zstd.input;
+ ZSTD_outBuffer *output = &cs->u.zstd.output;
+
+ input->src = (void *) unconstify(char *, data);
+ input->size = dLen;
+ input->pos = 0;
+
+ while (input->pos != input->size)
+ {
+ size_t res;
+
+ res = ZSTD_compressStream2(cs->u.zstd.cstream, output,
+ input, ZSTD_e_continue);
+
+ if (output->pos == output->size ||
+ input->pos != input->size)
+ {
+ /*
+ * Extra paranoia: avoid zero-length chunks, since a zero length
+ * chunk is the EOF marker in the custom format. This should never
+ * happen but...
+ */
+ if (output->pos > 0)
+ cs->writeF(AH, output->dst, output->pos);
+
+ output->pos = 0;
+ }
+
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ }
+}
+
+/* Read data from a compressed zstd archive */
+static void
+ReadDataFromArchiveZstd(ArchiveHandle *AH, ReadFunc readF)
+{
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ size_t res;
+ size_t input_size;
+
+ dstream = ZSTD_createDStream();
+ if (dstream == NULL)
+ fatal("could not initialize compression library");
+
+ input_size = ZSTD_DStreamInSize();
+ input.src = pg_malloc(input_size);
+
+ output.size = ZSTD_DStreamOutSize();
+ output.dst = pg_malloc(output.size);
+
+ /* read compressed data */
+ for (;;)
+ {
+ size_t cnt;
+
+ input.size = input_size; // XXX: the buffer can grow, we shouldn't keep resetting it to the original value..
+ cnt = readF(AH, (char **)unconstify(void **, &input.src), &input.size);
+ input.pos = 0;
+ input.size = cnt;
+
+ if (cnt == 0)
+ break;
+
+ while (input.pos < input.size)
+ {
+ /* decompress */
+ output.pos = 0;
+ res = ZSTD_decompressStream(dstream, &output, &input);
+
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+
+ /* write to output handle */
+ ((char *)output.dst)[output.pos] = '\0';
+ ahwrite(output.dst, 1, output.pos, AH);
+ // if (res == 0)
+ // break;
+ }
+ }
+
+ pg_free(unconstify(void *, input.src));
+ pg_free(output.dst);
+}
+
+#endif /* HAVE_LIBZSTD */
+
#ifdef HAVE_LIBZ
/*
* Functions for zlib compressed output.
@@ -411,6 +655,19 @@ struct cfp
#ifdef HAVE_LIBZ
gzFile compressedfp;
#endif
+
+#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
+ /* This is a normal file to which we read/write compressed data */
+ struct {
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
+#endif
+
};
static int hasSuffix(const char *filename);
@@ -525,6 +782,31 @@ cfopen(const char *path, const char *mode, Compress *compression)
return fp;
#endif
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ fp->zstd.fp = fopen(path, mode);
+ if (fp->zstd.fp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ else if (mode[0] == 'w' || mode[0] == 'a' ||
+ strchr(mode, '+') != NULL)
+ {
+ fp->zstd.output.size = ZSTD_CStreamOutSize();
+ fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
+ fp->zstd.cstream = ZstdCStreamParams(compression);
+ }
+ else if (strchr(mode, 'r'))
+ {
+ fp->zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
+ fp->zstd.dstream = ZSTD_createDStream();
+ if (fp->zstd.dstream == NULL)
+ fatal("could not initialize compression library");
+ } // XXX else: bad mode
+ return fp;
+#endif
+
case COMPR_ALG_NONE:
fp->uncompressedfp = fopen(path, mode);
if (fp->uncompressedfp == NULL)
@@ -576,6 +858,31 @@ cfdopen(int fd, const char *mode, Compress *compression)
return fp;
#endif
+#ifdef HAVE_LIBZSTD
+ case COMPR_ALG_ZSTD:
+ fp->zstd.fp = fdopen(fd, mode);
+ if (fp->zstd.fp == NULL)
+ {
+ free_keep_errno(fp);
+ fp = NULL;
+ }
+ else if (mode[0] == 'w' || mode[0] == 'a' ||
+ strchr(mode, '+') != NULL)
+ {
+ fp->zstd.output.size = ZSTD_CStreamOutSize();
+ fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
+ fp->zstd.cstream = ZstdCStreamParams(compression);
+ }
+ else if (strchr(mode, 'r'))
+ {
+ fp->zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
+ fp->zstd.dstream = ZSTD_createDStream();
+ if (fp->zstd.dstream == NULL)
+ fatal("could not initialize compression library");
+ } // XXX else: bad mode
+ return fp;
+#endif
+
case COMPR_ALG_NONE:
fp->uncompressedfp = fdopen(fd, mode);
if (fp->uncompressedfp == NULL)
@@ -617,6 +924,68 @@ cfread(void *ptr, int size, cfp *fp)
}
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t input_size = ZSTD_DStreamInSize();
+ /* input_size is the allocated size */
+ size_t res, cnt;
+
+ output->size = size;
+ output->dst = ptr;
+ output->pos = 0;
+
+ for (;;)
+ {
+ Assert(input->pos <= input->size);
+ Assert(input->size <= input_size);
+
+ /* If the input is completely consumed, start back at the beginning */
+ if (input->pos == input->size)
+ {
+ /* input->size is size produced by "fread" */
+ input->size = 0;
+ /* input->pos is position consumed by decompress */
+ input->pos = 0;
+ }
+
+ /* read compressed data if we must produce more input */
+ if (input->pos == input->size)
+ {
+ cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp);
+ input->size = cnt;
+
+ /* If we have no input to consume, we're done */
+ if (cnt == 0)
+ break;
+ }
+
+ Assert(cnt >= 0);
+ Assert(input->size <= input_size);
+
+ /* Now consume as much as possible */
+ for ( ; input->pos < input->size; )
+ {
+ /* decompress */
+ res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ if (res == 0)
+ break; /* End of frame */
+ if (output->pos == output->size)
+ break; /* No more room for output */
+ if (ZSTD_isError(res))
+ fatal("could not decompress data: %s", ZSTD_getErrorName(res));
+ }
+
+ if (output->pos == output->size)
+ break; /* We read all the data that fits */
+ }
+
+ return output->pos;
+ }
+#endif
+
ret = fread(ptr, 1, size, fp->uncompressedfp);
if (ret != size && !feof(fp->uncompressedfp))
READ_ERROR_EXIT(fp->uncompressedfp);
@@ -630,6 +999,35 @@ cfwrite(const void *ptr, int size, cfp *fp)
if (fp->compressedfp)
return gzwrite(fp->compressedfp, ptr, size);
#endif
+
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ size_t res, cnt;
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+
+ input->src = ptr;
+ input->size = size;
+ input->pos = 0;
+
+ /* Consume all input, and flush later */
+ while (input->pos != input->size)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ }
+
+ return size;
+ }
+#endif
+
return fwrite(ptr, 1, size, fp->uncompressedfp);
}
@@ -652,6 +1050,21 @@ cfgetc(cfp *fp)
return ret;
}
#endif
+
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ if (cfread(&ret, 1, fp) != 1)
+ {
+ if (feof(fp->zstd.fp))
+ fatal("could not read from input file: end of file");
+ else
+ fatal("could not read from input file: %s", strerror(errno));
+ }
+ return ret;
+ }
+#endif
+
ret = fgetc(fp->uncompressedfp);
if (ret == EOF)
READ_ERROR_EXIT(fp->uncompressedfp);
@@ -665,6 +1078,31 @@ cfgets(cfp *fp, char *buf, int len)
if (fp->compressedfp)
return gzgets(fp->compressedfp, buf, len);
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ /*
+ * Read one byte at a time until newline or EOF.
+ * This is only used to read the list of blobs, and the I/O is
+ * buffered anyway.
+ */
+ int i, res;
+ for (i = 0; i < len - 1; ++i)
+ {
+ res = cfread(&buf[i], 1, fp);
+ if (res != 1)
+ break;
+ if (buf[i] == '\n')
+ {
+ ++i;
+ break;
+ }
+ }
+ buf[i] = '\0';
+ return i > 0 ? buf : 0;
+ }
+#endif
+
return fgets(buf, len, fp->uncompressedfp);
}
@@ -688,6 +1126,44 @@ cfclose(cfp *fp)
}
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ {
+ ZSTD_outBuffer *output = &fp->zstd.output;
+ ZSTD_inBuffer *input = &fp->zstd.input;
+ size_t res, cnt;
+
+ if (fp->zstd.cstream)
+ {
+ for (;;)
+ {
+ output->pos = 0;
+ res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ if (ZSTD_isError(res))
+ fatal("could not compress data: %s", ZSTD_getErrorName(res));
+ cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ if (cnt != output->pos)
+ fatal("could not write data: %s", strerror(errno));
+ if (res == 0)
+ break;
+ }
+
+ ZSTD_freeCStream(fp->zstd.cstream);
+ pg_free(fp->zstd.output.dst);
+ }
+
+ if (fp->zstd.dstream)
+ {
+ ZSTD_freeDStream(fp->zstd.dstream);
+ pg_free(unconstify(void *, fp->zstd.input.src));
+ }
+
+ result = fclose(fp->zstd.fp);
+ fp->zstd.fp = NULL;
+ return result;
+ }
+#endif
+
result = fclose(fp->uncompressedfp);
fp->uncompressedfp = NULL;
free_keep_errno(fp);
@@ -702,6 +1178,10 @@ cfeof(cfp *fp)
return gzeof(fp->compressedfp);
#endif
+#ifdef HAVE_LIBZSTD
+ if (fp->zstd.fp)
+ return feof(fp->zstd.fp);
+#endif
return feof(fp->uncompressedfp);
}
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index f2390b7937..19ff6248d5 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -77,6 +77,7 @@ typedef enum
COMPR_ALG_DEFAULT = -1,
COMPR_ALG_NONE,
COMPR_ALG_LIBZ,
+ COMPR_ALG_ZSTD,
} CompressionAlgorithm;
/* Should be called "method" or "library" ? */
@@ -88,6 +89,19 @@ typedef struct Compress {
* are all integer, though.
*/
bool level_set;
+
+ /*
+ * This could be a union across all compress algorithms, but
+ * keeping as separate structs allows checking that options are
+ * not specified for a different algorithm than selected.
+ */
+
+ struct {
+ bool longdistance;
+ bool checksum;
+ bool rsyncable;
+ int threads;
+ } zstd;
} Compress;
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 9f511b49b9..da2eb53277 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -56,6 +56,10 @@ typedef struct _z_stream
typedef z_stream *z_streamp;
#endif
+#ifdef HAVE_LIBZSTD
+#include <zstd.h>
+#endif /* HAVE_LIBZSTD */
+
/* Data block types */
#define BLK_DATA 1
#define BLK_BLOBS 3
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 75c1bf22e4..b8efeb8ca7 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -393,8 +393,12 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
if (!cfp)
fatal("could not open input file \"%s\": %m", filename);
- buf = pg_malloc(ZLIB_OUT_SIZE);
- buflen = ZLIB_OUT_SIZE;
+ /*
+ * zstd prefers a 128kB buffer. The allocation cannot happen in
+ * cfread, since the "cfp" is an opaque type.
+ */
+ buf = pg_malloc(128*1024);
+ buflen = 128*1024;
while ((cnt = cfread(buf, buflen, cfp)))
{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75985fd4d3..7c2f7a9ca3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -356,6 +356,12 @@ parse_compression(const char *optarg, Compress *compress)
compress->level = atoi(1+eq);
compress->level_set = true;
}
+ else if (strncmp(optarg, "zstdlong", len) == 0)
+ compress->zstd.longdistance = atoi(1+eq);
+ else if (strncmp(optarg, "checksum", len) == 0)
+ compress->zstd.checksum = atoi(1+eq);
+ else if (strncmp(optarg, "threads", len) == 0)
+ compress->zstd.threads = atoi(1+eq);
else
{
pg_log_error("unknown compression setting: %s", optarg);
@@ -367,11 +373,31 @@ parse_compression(const char *optarg, Compress *compress)
break;
}
+ /* XXX: zstd will check its own compression level later */
+ if (compress->alg != COMPR_ALG_ZSTD)
+ {
+ Compress nullopts = {0};
+
+ if (compress->level < 0 || compress->level > 9)
+ {
+ pg_log_error("compression level must be in range 0..9");
+ exit_nicely(1);
+ }
+
+// XXX: needs to set default alg first
+ if (memcmp(&compress->zstd, &nullopts.zstd, sizeof(nullopts.zstd)) != 0)
+ {
+ pg_log_error("compression option not supported with this algorithm");
+ exit_nicely(1);
+ }
+ }
+
if (!compress->level_set)
{ // XXX
const int default_compress_level[] = {
0, /* COMPR_ALG_NONE */
Z_DEFAULT_COMPRESSION, /* COMPR_ALG_ZLIB */
+ 0, // XXX: ZSTD_CLEVEL_DEFAULT, /* COMPR_ALG_ZSTD */
};
compress->level = default_compress_level[compress->alg];
@@ -764,6 +790,11 @@ main(int argc, char **argv)
compress.alg = COMPR_ALG_LIBZ;
compress.level = Z_DEFAULT_COMPRESSION;
#endif
+
+#ifdef HAVE_LIBZSTD
+ compress.alg = COMPR_ALG_ZSTD; // Set default for testing purposes
+ compress.level = ZSTD_CLEVEL_DEFAULT;
+#endif
}
else
{
@@ -780,6 +811,14 @@ main(int argc, char **argv)
}
#endif
+#ifndef HAVE_LIBZSTD
+ if (compress.alg == COMPR_ALG_ZSTD)
+ {
+ pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
+ compress.alg = 0;
+ }
+#endif
+
/*
* If emitting an archive format, we always want to emit a DATABASE item,
* in case --create is specified at pg_restore time.
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index de8f838e53..da35415c72 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -346,6 +346,9 @@
/* Define to 1 if you have the `z' library (-lz). */
#undef HAVE_LIBZ
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+#undef HAVE_LIBZSTD
+
/* Define to 1 if you have the `link' function. */
#undef HAVE_LINK
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 22d6abd367..a101366b4c 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -307,6 +307,7 @@ sub GenerateFiles
HAVE_LIBXML2 => undef,
HAVE_LIBXSLT => undef,
HAVE_LIBZ => $self->{options}->{zlib} ? 1 : undef,
+ HAVE_LIBZSTD => $self->{options}->{zstd} ? 1 : undef,
HAVE_LINK => undef,
HAVE_LOCALE_T => 1,
HAVE_LONG_INT_64 => undef,
--
2.17.0
0007-fix-comments.patchtext/x-diff; charset=us-asciiDownload
From fe12ba8f0ec663f4778e6b46053739ad4ec20514 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Thu, 24 Dec 2020 00:01:43 -0600
Subject: [PATCH 07/20] fix comments
---
src/bin/pg_dump/compress_io.c | 15 +++++++--------
src/bin/pg_dump/pg_backup_directory.c | 4 ++--
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 285f554c1a..fa94148cdf 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -13,7 +13,7 @@
* friends, providing an interface similar to those, but abstracts away
* the possible compression. Both APIs use libz for the compression, but
* the second API uses gzip headers, so the resulting files can be easily
- * manipulated with the gzip utility.
+ * manipulated with the gzip utility. XXX
*
* Compressor API
* --------------
@@ -41,9 +41,9 @@
* libz's gzopen() APIs. It allows you to use the same functions for
* compressed and uncompressed streams. cfopen_read() first tries to open
* the file with given name, and if it fails, it tries to open the same
- * file with the .gz suffix. cfopen_write() opens a file for writing, an
+ * file with a compressed suffix. cfopen_write() opens a file for writing, an
* extra argument specifies if the file should be compressed, and adds the
- * .gz suffix to the filename if so. This allows you to easily handle both
+ * compressed suffix to the filename if so. This allows you to easily handle both
* compressed and uncompressed files.
*
* IDENTIFICATION
@@ -646,8 +646,8 @@ WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
*/
/*
- * cfp represents an open stream, wrapping the underlying FILE or gzFile
- * pointer. This is opaque to the callers.
+ * cfp represents an open stream, wrapping the underlying compressed or
+ * uncompressed file object. This is opaque to the callers.
*/
struct cfp
{
@@ -687,8 +687,7 @@ free_keep_errno(void *p)
* be either "r" or "rb".
*
* If the file at 'path' does not exist, we search with compressed suffix (if 'path'
- * doesn't already have one) and try again. So if you pass "foo" as 'path',
- * this will open either "foo" or "foo.gz".
+ * doesn't already have one) and try again.
*
* On failure, return NULL with an error code in errno.
*/
@@ -745,7 +744,7 @@ cfopen_write(const char *path, const char *mode, Compress *compression)
}
/*
- * Opens file 'path' in 'mode'. If 'compression' is non-zero, the file
+ * Opens file 'path' in 'mode'. If 'alg' is COMPR_ALG_ZLIB, the file
* is opened with libz gzopen(), otherwise with plain fopen().
*
* On failure, return NULL with an error code in errno.
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index b8efeb8ca7..f0ded2421d 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -6,8 +6,8 @@
* for the TOC, and a separate file for each data entry, named "<oid>.dat".
* Large objects (BLOBs) are stored in separate files named "blob_<oid>.dat",
* and there's a plain-text TOC file for them called "blobs.toc". If
- * compression is used, each data file is individually compressed and the
- * ".gz" suffix is added to the filenames. The TOC files are never
+ * compression is used, each data file is individually compressed with a
+ * suffix is added to the filenames. The TOC files are never
* compressed by pg_dump, however they are accepted with the .gz suffix too,
* in case the user has manually compressed them with 'gzip'.
*
--
2.17.0
0008-union-with-a-CompressionAlgorithm-alg.patchtext/x-diff; charset=us-asciiDownload
From 44b3ed951859072b8d814d0439565187bf960b7b Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 21 Dec 2020 00:11:43 -0600
Subject: [PATCH 08/20] union{} with a CompressionAlgorithm alg
---
src/bin/pg_dump/compress_io.c | 200 ++++++++++++++++++----------------
src/bin/pg_dump/pg_dump.c | 2 +-
2 files changed, 106 insertions(+), 96 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index fa94148cdf..e07436bc21 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -651,23 +651,27 @@ WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
*/
struct cfp
{
- FILE *uncompressedfp;
+ CompressionAlgorithm alg;
+
+ union {
+ FILE *fp;
+
#ifdef HAVE_LIBZ
- gzFile compressedfp;
+ gzFile gzfp;
#endif
-#ifdef HAVE_LIBZSTD // XXX: this should be a union with a CompressionAlgorithm alg?
- /* This is a normal file to which we read/write compressed data */
- struct {
- FILE *fp;
- // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
- ZSTD_DStream *dstream;
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
- } zstd;
+#ifdef HAVE_LIBZSTD
+ struct {
+ /* This is a normal file to which we read/write compressed data */
+ FILE *fp;
+ // XXX: use one separate ZSTD_CStream per thread: disable on windows ?
+ ZSTD_CStream *cstream;
+ ZSTD_DStream *dstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+ } zstd;
#endif
-
+ } u;
};
static int hasSuffix(const char *filename);
@@ -754,6 +758,8 @@ cfopen(const char *path, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc0(sizeof(cfp));
+ fp->alg = compression->alg;
+
switch (compression->alg)
{
#ifdef HAVE_LIBZ
@@ -765,15 +771,15 @@ cfopen(const char *path, const char *mode, Compress *compression)
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
mode, compression->level);
- fp->compressedfp = gzopen(path, mode_compression);
+ fp->u.gzfp = gzopen(path, mode_compression);
}
else
{
/* don't specify a level, just use the zlib default */
- fp->compressedfp = gzopen(path, mode);
+ fp->u.gzfp = gzopen(path, mode);
}
- if (fp->compressedfp == NULL)
+ if (fp->u.gzfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -783,8 +789,8 @@ cfopen(const char *path, const char *mode, Compress *compression)
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- fp->zstd.fp = fopen(path, mode);
- if (fp->zstd.fp == NULL)
+ fp->u.zstd.fp = fopen(path, mode);
+ if (fp->u.zstd.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -792,23 +798,23 @@ cfopen(const char *path, const char *mode, Compress *compression)
else if (mode[0] == 'w' || mode[0] == 'a' ||
strchr(mode, '+') != NULL)
{
- fp->zstd.output.size = ZSTD_CStreamOutSize();
- fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
- fp->zstd.cstream = ZstdCStreamParams(compression);
+ fp->u.zstd.output.size = ZSTD_CStreamOutSize();
+ fp->u.zstd.output.dst = pg_malloc0(fp->u.zstd.output.size);
+ fp->u.zstd.cstream = ZstdCStreamParams(compression);
}
else if (strchr(mode, 'r'))
{
- fp->zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
- fp->zstd.dstream = ZSTD_createDStream();
- if (fp->zstd.dstream == NULL)
+ fp->u.zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
+ fp->u.zstd.dstream = ZSTD_createDStream();
+ if (fp->u.zstd.dstream == NULL)
fatal("could not initialize compression library");
} // XXX else: bad mode
return fp;
#endif
case COMPR_ALG_NONE:
- fp->uncompressedfp = fopen(path, mode);
- if (fp->uncompressedfp == NULL)
+ fp->u.fp = fopen(path, mode);
+ if (fp->u.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -830,6 +836,8 @@ cfdopen(int fd, const char *mode, Compress *compression)
{
cfp *fp = pg_malloc0(sizeof(cfp));
+ fp->alg = compression->alg;
+
switch (compression->alg)
{
#ifdef HAVE_LIBZ
@@ -841,15 +849,15 @@ cfdopen(int fd, const char *mode, Compress *compression)
snprintf(mode_compression, sizeof(mode_compression), "%s%d",
mode, compression->level);
- fp->compressedfp = gzdopen(fd, mode_compression);
+ fp->u.gzfp = gzdopen(fd, mode_compression);
}
else
{
/* don't specify a level, just use the zlib default */
- fp->compressedfp = gzdopen(fd, mode);
+ fp->u.gzfp = gzdopen(fd, mode);
}
- if (fp->compressedfp == NULL)
+ if (fp->u.gzfp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -859,8 +867,8 @@ cfdopen(int fd, const char *mode, Compress *compression)
#ifdef HAVE_LIBZSTD
case COMPR_ALG_ZSTD:
- fp->zstd.fp = fdopen(fd, mode);
- if (fp->zstd.fp == NULL)
+ fp->u.zstd.fp = fdopen(fd, mode);
+ if (fp->u.zstd.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -868,23 +876,23 @@ cfdopen(int fd, const char *mode, Compress *compression)
else if (mode[0] == 'w' || mode[0] == 'a' ||
strchr(mode, '+') != NULL)
{
- fp->zstd.output.size = ZSTD_CStreamOutSize();
- fp->zstd.output.dst = pg_malloc0(fp->zstd.output.size);
- fp->zstd.cstream = ZstdCStreamParams(compression);
+ fp->u.zstd.output.size = ZSTD_CStreamOutSize();
+ fp->u.zstd.output.dst = pg_malloc0(fp->u.zstd.output.size);
+ fp->u.zstd.cstream = ZstdCStreamParams(compression);
}
else if (strchr(mode, 'r'))
{
- fp->zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
- fp->zstd.dstream = ZSTD_createDStream();
- if (fp->zstd.dstream == NULL)
+ fp->u.zstd.input.src = pg_malloc0(ZSTD_DStreamInSize());
+ fp->u.zstd.dstream = ZSTD_createDStream();
+ if (fp->u.zstd.dstream == NULL)
fatal("could not initialize compression library");
} // XXX else: bad mode
return fp;
#endif
case COMPR_ALG_NONE:
- fp->uncompressedfp = fdopen(fd, mode);
- if (fp->uncompressedfp == NULL)
+ fp->u.fp = fdopen(fd, mode);
+ if (fp->u.fp == NULL)
{
free_keep_errno(fp);
fp = NULL;
@@ -908,13 +916,13 @@ cfread(void *ptr, int size, cfp *fp)
return 0;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzread(fp->compressedfp, ptr, size);
- if (ret != size && !gzeof(fp->compressedfp))
+ ret = gzread(fp->u.gzfp, ptr, size);
+ if (ret != size && !gzeof(fp->u.gzfp))
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
fatal("could not read from input file: %s",
errnum == Z_ERRNO ? strerror(errno) : errmsg);
@@ -924,10 +932,10 @@ cfread(void *ptr, int size, cfp *fp)
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t input_size = ZSTD_DStreamInSize();
/* input_size is the allocated size */
size_t res, cnt;
@@ -953,7 +961,7 @@ cfread(void *ptr, int size, cfp *fp)
/* read compressed data if we must produce more input */
if (input->pos == input->size)
{
- cnt = fread(unconstify(void *, input->src), 1, input_size, fp->zstd.fp);
+ cnt = fread(unconstify(void *, input->src), 1, input_size, fp->u.zstd.fp);
input->size = cnt;
/* If we have no input to consume, we're done */
@@ -968,7 +976,7 @@ cfread(void *ptr, int size, cfp *fp)
for ( ; input->pos < input->size; )
{
/* decompress */
- res = ZSTD_decompressStream(fp->zstd.dstream, output, input);
+ res = ZSTD_decompressStream(fp->u.zstd.dstream, output, input);
if (res == 0)
break; /* End of frame */
if (output->pos == output->size)
@@ -985,9 +993,9 @@ cfread(void *ptr, int size, cfp *fp)
}
#endif
- ret = fread(ptr, 1, size, fp->uncompressedfp);
- if (ret != size && !feof(fp->uncompressedfp))
- READ_ERROR_EXIT(fp->uncompressedfp);
+ ret = fread(ptr, 1, size, fp->u.fp);
+ if (ret != size && !feof(fp->u.fp))
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -995,16 +1003,16 @@ int
cfwrite(const void *ptr, int size, cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzwrite(fp->compressedfp, ptr, size);
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzwrite(fp->u.gzfp, ptr, size);
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
size_t res, cnt;
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
input->src = ptr;
input->size = size;
@@ -1014,11 +1022,11 @@ cfwrite(const void *ptr, int size, cfp *fp)
while (input->pos != input->size)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_continue);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_continue);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
}
@@ -1027,7 +1035,7 @@ cfwrite(const void *ptr, int size, cfp *fp)
}
#endif
- return fwrite(ptr, 1, size, fp->uncompressedfp);
+ return fwrite(ptr, 1, size, fp->u.fp);
}
int
@@ -1036,12 +1044,12 @@ cfgetc(cfp *fp)
int ret;
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- ret = gzgetc(fp->compressedfp);
+ ret = gzgetc(fp->u.gzfp);
if (ret == EOF)
{
- if (!gzeof(fp->compressedfp))
+ if (!gzeof(fp->u.gzfp))
fatal("could not read from input file: %s", strerror(errno));
else
fatal("could not read from input file: end of file");
@@ -1051,11 +1059,11 @@ cfgetc(cfp *fp)
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
if (cfread(&ret, 1, fp) != 1)
{
- if (feof(fp->zstd.fp))
+ if (feof(fp->u.zstd.fp))
fatal("could not read from input file: end of file");
else
fatal("could not read from input file: %s", strerror(errno));
@@ -1064,9 +1072,9 @@ cfgetc(cfp *fp)
}
#endif
- ret = fgetc(fp->uncompressedfp);
+ ret = fgetc(fp->u.fp);
if (ret == EOF)
- READ_ERROR_EXIT(fp->uncompressedfp);
+ READ_ERROR_EXIT(fp->u.fp);
return ret;
}
@@ -1074,11 +1082,12 @@ char *
cfgets(cfp *fp, char *buf, int len)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzgets(fp->compressedfp, buf, len);
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzgets(fp->u.gzfp, buf, len);
#endif
+
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
/*
* Read one byte at a time until newline or EOF.
@@ -1102,7 +1111,7 @@ cfgets(cfp *fp, char *buf, int len)
}
#endif
- return fgets(buf, len, fp->uncompressedfp);
+ return fgets(buf, len, fp->u.fp);
}
/* Close the given compressed or uncompressed stream; return 0 on success. */
@@ -1117,54 +1126,54 @@ cfclose(cfp *fp)
return EOF;
}
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
- result = gzclose(fp->compressedfp);
- fp->compressedfp = NULL;
+ result = gzclose(fp->u.gzfp);
+ fp->u.gzfp = NULL;
return result;
}
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
+ if (fp->alg == COMPR_ALG_ZSTD)
{
- ZSTD_outBuffer *output = &fp->zstd.output;
- ZSTD_inBuffer *input = &fp->zstd.input;
+ ZSTD_outBuffer *output = &fp->u.zstd.output;
+ ZSTD_inBuffer *input = &fp->u.zstd.input;
size_t res, cnt;
- if (fp->zstd.cstream)
+ if (fp->u.zstd.cstream)
{
for (;;)
{
output->pos = 0;
- res = ZSTD_compressStream2(fp->zstd.cstream, output, input, ZSTD_e_end);
+ res = ZSTD_compressStream2(fp->u.zstd.cstream, output, input, ZSTD_e_end);
if (ZSTD_isError(res))
fatal("could not compress data: %s", ZSTD_getErrorName(res));
- cnt = fwrite(output->dst, 1, output->pos, fp->zstd.fp);
+ cnt = fwrite(output->dst, 1, output->pos, fp->u.zstd.fp);
if (cnt != output->pos)
fatal("could not write data: %s", strerror(errno));
if (res == 0)
break;
}
- ZSTD_freeCStream(fp->zstd.cstream);
- pg_free(fp->zstd.output.dst);
+ ZSTD_freeCStream(fp->u.zstd.cstream);
+ pg_free(fp->u.zstd.output.dst);
}
- if (fp->zstd.dstream)
+ if (fp->u.zstd.dstream)
{
- ZSTD_freeDStream(fp->zstd.dstream);
- pg_free(unconstify(void *, fp->zstd.input.src));
+ ZSTD_freeDStream(fp->u.zstd.dstream);
+ pg_free(unconstify(void *, fp->u.zstd.input.src));
}
- result = fclose(fp->zstd.fp);
- fp->zstd.fp = NULL;
+ result = fclose(fp->u.zstd.fp);
+ fp->u.zstd.fp = NULL;
return result;
}
#endif
- result = fclose(fp->uncompressedfp);
- fp->uncompressedfp = NULL;
+ result = fclose(fp->u.fp);
+ fp->u.fp = NULL;
free_keep_errno(fp);
return result;
}
@@ -1173,25 +1182,26 @@ int
cfeof(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
- return gzeof(fp->compressedfp);
+ if (fp->alg == COMPR_ALG_LIBZ)
+ return gzeof(fp->u.gzfp);
#endif
#ifdef HAVE_LIBZSTD
- if (fp->zstd.fp)
- return feof(fp->zstd.fp);
+ if (fp->alg == COMPR_ALG_ZSTD)
+ return feof(fp->u.zstd.fp);
#endif
- return feof(fp->uncompressedfp);
+
+ return feof(fp->u.fp);
}
const char *
get_cfp_error(cfp *fp)
{
#ifdef HAVE_LIBZ
- if (fp->compressedfp)
+ if (fp->alg == COMPR_ALG_LIBZ)
{
int errnum;
- const char *errmsg = gzerror(fp->compressedfp, &errnum);
+ const char *errmsg = gzerror(fp->u.gzfp, &errnum);
if (errnum != Z_ERRNO)
return errmsg;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7c2f7a9ca3..5e009e5854 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -397,7 +397,7 @@ parse_compression(const char *optarg, Compress *compress)
const int default_compress_level[] = {
0, /* COMPR_ALG_NONE */
Z_DEFAULT_COMPRESSION, /* COMPR_ALG_ZLIB */
- 0, // XXX: ZSTD_CLEVEL_DEFAULT, /* COMPR_ALG_ZSTD */
+ 0, // #ifdef LIBZSTD ZSTD_CLEVEL_DEFAULT, /* COMPR_ALG_ZSTD */
};
compress->level = default_compress_level[compress->alg];
--
2.17.0
0009-Move-zlib-into-the-union.patchtext/x-diff; charset=us-asciiDownload
From c43384ebc1ff47536b207374ed472182ff4ae0e8 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 11 Dec 2020 22:22:31 -0600
Subject: [PATCH 09/20] Move zlib into the union{}
---
src/bin/pg_dump/compress_io.c | 56 ++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index e07436bc21..ad085a7d54 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -91,23 +91,25 @@ struct CompressorState
CompressionAlgorithm comprAlg;
WriteFunc writeF;
+ union {
#ifdef HAVE_LIBZ
- z_streamp zp;
- char *zlibOut;
- size_t zlibOutSize;
+ struct {
+ z_streamp zp;
+ char *zlibOut;
+ size_t zlibOutSize;
+ } zlib;
#endif
#ifdef HAVE_LIBZSTD
- union {
+ /* This is used for compression but not decompression */
struct {
- ZSTD_outBuffer output;
- ZSTD_inBuffer input;
// XXX: use one separate ZSTD_CStream per thread: disable on windows ?
- ZSTD_CStream *cstream;
+ ZSTD_CStream *cstream;
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
} zstd;
- } u;
#endif
-
+ } u;
};
/* Routines that support zlib compressed data I/O */
@@ -452,7 +454,7 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
{
z_streamp zp;
- zp = cs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
+ zp = cs->u.zlib.zp = (z_streamp) pg_malloc(sizeof(z_stream));
zp->zalloc = Z_NULL;
zp->zfree = Z_NULL;
zp->opaque = Z_NULL;
@@ -462,22 +464,22 @@ InitCompressorZlib(CompressorState *cs, Compress *compress)
* actually allocate one extra byte because some routines want to append a
* trailing zero byte to the zlib output.
*/
- cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
- cs->zlibOutSize = ZLIB_OUT_SIZE;
+ cs->u.zlib.zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
+ cs->u.zlib.zlibOutSize = ZLIB_OUT_SIZE;
if (deflateInit(zp, compress->level) != Z_OK)
fatal("could not initialize compression library: %s",
zp->msg);
/* Just be paranoid - maybe End is called after Start, with no Write */
- zp->next_out = (void *) cs->zlibOut;
- zp->avail_out = cs->zlibOutSize;
+ zp->next_out = (void *) cs->u.zlib.zlibOut;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
static void
EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
{
- z_streamp zp = cs->zp;
+ z_streamp zp = cs->u.zlib.zp;
zp->next_in = NULL;
zp->avail_in = 0;
@@ -488,23 +490,23 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
if (deflateEnd(zp) != Z_OK)
fatal("could not close compression stream: %s", zp->msg);
- free(cs->zlibOut);
- free(cs->zp);
+ free(cs->u.zlib.zlibOut);
+ free(cs->u.zlib.zp);
}
static void
DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
{
- z_streamp zp = cs->zp;
- char *out = cs->zlibOut;
+ z_streamp zp = cs->u.zlib.zp;
+ char *out = cs->u.zlib.zlibOut;
int res = Z_OK;
- while (cs->zp->avail_in != 0 || flush)
+ while (cs->u.zlib.zp->avail_in != 0 || flush)
{
res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
if (res == Z_STREAM_ERROR)
fatal("could not compress data: %s", zp->msg);
- if ((flush && (zp->avail_out < cs->zlibOutSize))
+ if ((flush && (zp->avail_out < cs->u.zlib.zlibOutSize))
|| (zp->avail_out == 0)
|| (zp->avail_in != 0)
)
@@ -514,18 +516,18 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
* chunk is the EOF marker in the custom format. This should never
* happen but...
*/
- if (zp->avail_out < cs->zlibOutSize)
+ if (zp->avail_out < cs->u.zlib.zlibOutSize)
{
/*
* Any write function should do its own error checking but to
* make sure we do a check here as well...
*/
- size_t len = cs->zlibOutSize - zp->avail_out;
+ size_t len = cs->u.zlib.zlibOutSize - zp->avail_out;
cs->writeF(AH, out, len);
}
zp->next_out = (void *) out;
- zp->avail_out = cs->zlibOutSize;
+ zp->avail_out = cs->u.zlib.zlibOutSize;
}
if (res == Z_STREAM_END)
@@ -537,8 +539,8 @@ static void
WriteDataToArchiveZlib(ArchiveHandle *AH, CompressorState *cs,
const char *data, size_t dLen)
{
- cs->zp->next_in = (void *) unconstify(char *, data);
- cs->zp->avail_in = dLen;
+ cs->u.zlib.zp->next_in = (void *) unconstify(char *, data);
+ cs->u.zlib.zp->avail_in = dLen;
DeflateCompressorZlib(AH, cs, false);
}
@@ -898,7 +900,7 @@ cfdopen(int fd, const char *mode, Compress *compression)
fp = NULL;
}
else
- setvbuf(fp->uncompressedfp, NULL, _IONBF, 0);
+ setvbuf(fp->u.fp, NULL, _IONBF, 0);
return fp;
default:
--
2.17.0
4 янв. 2021 г., в 07:53, Justin Pryzby <pryzby@telsasoft.com> написал(а):
Note, there's currently several "compression" patches in CF app. This patch
seems to be independent of the others, but probably shouldn't be totally
uncoordinated (like adding lz4 in one and ztsd in another might be poor
execution).https://commitfest.postgresql.org/31/2897/
- Faster pglz compression
https://commitfest.postgresql.org/31/2813/
- custom compression methods for toast
https://commitfest.postgresql.org/31/2773/
- libpq compression
I think that's downside of our development system: patch authors do not want to create dependencies on other patches.
I'd say that both lz4 and zstd should be supported in TOAST, FPIs, libpq, and pg_dump. As to pglz - I think we should not proliferate it any further.
Lz4 and Zstd represent a different tradeoff actually. Basically, lz4 is so CPU-cheap that one should use it whenever they write to disk or network interface. Zstd represent an actual bandwith\CPU tradeoff.
Also, all patchsets do not touch important possibility - preexisting dictionary could radically improve compression of small data (event in pglz).
Some minor notes on patchset at this thread.
Libpq compression encountered some problems with memory consumption which required some extra config efforts. Did you measure memory usage for this patchset?
[PATCH 03/20] Support multiple compression algs/levels/opts..
abtracts -> abstracts
enum CompressionAlgorithm actually represent the very same thing as in "Custom compression methods"
Daniil, is levels definition compatible with libpq compression patch?
+typedef struct Compress {
+ CompressionAlgorithm alg;
+ int level;
+ /* Is a nondefault level set ? This is useful since different compression
+ * methods have different "default" levels. For now we assume the levels
+ * are all integer, though.
+ */
+ bool level_set;
+} Compress;
[PATCH 04/20] struct compressLibs
I think this directive would be correct.
+// #ifdef HAVE_LIBZ?
Here's extra comment
// && errno == ENOENT)
[PATCH 06/20] pg_dump: zstd compression
I'd propose to build with Zstd by default. It seems other patches do it this way. Though, I there are possible downsides.
Thanks for working on this! We will have very IO-efficient Postgres :)
Best regards, Andrey Borodin.
On Mon, Jan 04, 2021 at 11:04:57AM +0500, Andrey Borodin wrote:
4 янв. 2021 г., в 07:53, Justin Pryzby <pryzby@telsasoft.com> написал(а):
Note, there's currently several "compression" patches in CF app. This patch
seems to be independent of the others, but probably shouldn't be totally
uncoordinated (like adding lz4 in one and ztsd in another might be poor
execution).https://commitfest.postgresql.org/31/2897/
- Faster pglz compression
https://commitfest.postgresql.org/31/2813/
- custom compression methods for toast
https://commitfest.postgresql.org/31/2773/
- libpq compressionI think that's downside of our development system: patch authors do not want to create dependencies on other patches.
I think in these cases, someone who notices common/overlapping patches should
suggest that the authors review each other's work. In some cases, I think it's
appropriate to come up with a "shared" preliminary patch(es), which both (all)
patch authors can include as 0001 until its finalized and merged. That might
be true for some things like the tableam work, or the two "online checksum"
patches.
I'd say that both lz4 and zstd should be supported in TOAST, FPIs, libpq, and pg_dump. As to pglz - I think we should not proliferate it any further.
pg_basebackup came up as another use on another thread, I think related to
libpq protocol compression.
Libpq compression encountered some problems with memory consumption which
required some extra config efforts. Did you measure memory usage for this
patchset?
RAM use is not significantly different from zlib, except that zstd --long adds
more memory.
$ command time -v pg_dump -d ts -t ... -Fc -Z0 |wc -c
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:28.77
Maximum resident set size (kbytes): 40504
1397288924 # no compression: 1400MB
$ command time -v pg_dump -d ts -t ... -Fc |wc -c
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:37.17
Maximum resident set size (kbytes): 40504
132932415 # default (zlib) compression: 132 MB
$ command time -v ./pg_dump -d ts -t ... -Fc |wc -c
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:29.28
Maximum resident set size (kbytes): 40568
86048139 # zstd: 86MB
$ command time -v ./pg_dump -d ts -t ... -Fc -Z 'alg=zstd opt=zstdlong' |wc -c
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:30.49
Maximum resident set size (kbytes): 180332
72202937 # zstd long: 180MB
[PATCH 04/20] struct compressLibs
I think this directive would be correct.
+// #ifdef HAVE_LIBZ?
I'm not sure .. I'm thinking of making the COMPR_ALG_* always defined, and then
fail later if an operation is unsupported. There's an excessive number of
#ifdefs already, so the early commits are intended to minimize as far as
possible what's needed for each additional compression
algorithm(lib/method/whatever it's called). I haven't tested much with
pg_restore of files with unsupported compression libs.
[PATCH 06/20] pg_dump: zstd compression
I'd propose to build with Zstd by default. It seems other patches do it this way. Though, I there are possible downsides.
Yes...but the cfbot turns red if the patch require zstd, so it defaults to
off until it's included in the build environments (but for now, the main patch
isn't being tested).
Thanks for looking.
--
Justin
Hi!
On Jan 4, 2021, at 11:04 AM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:
Daniil, is levels definition compatible with libpq compression patch? +typedef struct Compress { + CompressionAlgorithm alg; + int level; + /* Is a nondefault level set ? This is useful since different compression + * methods have different "default" levels. For now we assume the levels + * are all integer, though. + */ + bool level_set; +} Compress;
Similarly to this patch, it is also possible to define the compression level at the initialization stage in libpq compression patch.
The difference is that in libpq compression patch the default compression level always equal to 1, independently of the chosen compression algorithm.
On Jan 4, 2021, at 11:04 AM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:
Libpq compression encountered some problems with memory consumption which required some extra config efforts.
On Jan 4, 2021, at 12:06 PM, Justin Pryzby <pryzby@telsasoft.com> wrote:
RAM use is not significantly different from zlib, except that zstd --long adds
more memory.
Regarding ZSTD memory usage:
Recently I’ve made a couple of tests of libpq compression with different ZLIB/ZSTD compression levels which shown that compressing/decompressing ZSTD w/ high compression levels
require to allocate more virtual (Commited_AS) memory, which may be exploited by malicious clients:
/messages/by-id/62527092-16BD-479F-B503-FA527AF3B0C2@yandex-team.ru
We can avoid high memory usage by limiting the max window size to 8MB. This should effectively disable the support of compression levels above 19:
/messages/by-id/6A45DFAA-1682-4EF2-B835-C5F46615EC49@yandex-team.ru
So maybe it is worthwhile to use similar restrictions in this patch.
—
Daniil Zakhlystov
On 1/4/21 3:53 AM, Justin Pryzby wrote:
About 89% smaller.
Did a quick code review of the patch. I have not yet taken it for a spin
yet and there are parts of the code I have not read yet.
## Is there any reason for this diff?
- cfp *fp = pg_malloc(sizeof(cfp));
+ cfp *fp = pg_malloc0(sizeof(cfp));
## Since we know have multiple returns in cfopen() I am not sure that
setting fp to NULL is still clearer than just returning NULL.
## I do not like that this pretends to support r+, w+ and a+ but does
not actually do so since it does not create an input stream in those cases.
else if (mode[0] == 'w' || mode[0] == 'a' ||
strchr(mode, '+') != NULL)
[...]
else if (strchr(mode, 'r'))
## Wouldn't cfread(), cfwrite(), cfgetc(), cfgets(), cfclose() and
cfeof() be cleaner with sitch statments similar to cfopen()?
## "/* Should be called "method" or "library" ? */"
Maybe, but personally I think algorithm is fine too.
## "Is a nondefault level set ?"
The PostgreSQL project does not use space before question mark (at least
not in English).
## Why isn't level_set just a local variable in parse_compression()? It
does not seem to be used elsewhere.
## Shouldn't we call the Compression variable in OpenArchive()
nocompress to match with the naming convention in other places.
And in general I wonder if we should not write "nocompression =
{COMPR_ALG_NONE}" rather than "nocompression = {0}".
## Why not use const on the pointers to Compression for functions like
cfopen()? As far as I can see several of them could be const.
## Shouldn't "AH->compression.alg = Z_DEFAULT_COMPRESSION" in ReadHead()
be "AH->compression.alg = COMPR_ALG_DEFAULT"?
Additionally I am not convinced that returning COMPR_ALG_DEFAULT will
even work but I have not had the time to test that theory yet. And in
general I am quite sceptical of that we really need of COMPR_ALG_DEFAULT.
## Some white space issues
Add spaces around plus in "atoi(1+eq)" and "pg_log_error("unknown
compression algorithm: %s", 1+eq)".
Add spaces around plus in parse_compression(), e.g. in "strlen(1+eq)".
## Shouldn't hasSuffix() take the current compression algorithm as a
parameter? Or alternatively look up which compression algorithm to use
from the suffix?
## Why support multiple ways to write zlib on the command line? I do not
see any advatange of being able to write it as libz.
## I feel renaming SaveOutput() to GetOutput() would make it more clear
what it does now that you have changed the return type.
## You have accidentally committed "-runstatedir" in configure. I have
no idea why we do not have it (maybe it is something Debian specific)
but even if we are going to add it it should not be in this patch. Same
with the parenthesis changes to LARGE_OFF_T.
## This is probably out of scope of your patch but I am not a fan of the
fallback logic in cfopen_read(). I feel ideally we should always know if
there is a suffix or not and not try to guess file names and do
pointless syscalls.
## COMPR_ALG_DEFAULT looks like it would error out for archive and
directory if someone has neither zlib nor zstandard. It feels like it
should default to uncompressed if we have neither. Or at least give a
better error message.
Note, there's currently several "compression" patches in CF app. This patch
seems to be independent of the others, but probably shouldn't be totally
uncoordinated (like adding lz4 in one and ztsd in another might be poor
execution).
A thought here is that maybe we want to use the same values for the
enums in all patches. Especially if we write the numeric value to pg
dump files.
Andreas
On 1/4/21 11:17 AM, Daniil Zakhlystov wrote:
Hi!
On Jan 4, 2021, at 11:04 AM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:
Daniil, is levels definition compatible with libpq compression patch? +typedef struct Compress { + CompressionAlgorithm alg; + int level; + /* Is a nondefault level set ? This is useful since different compression + * methods have different "default" levels. For now we assume the levels + * are all integer, though. + */ + bool level_set; +} Compress;Similarly to this patch, it is also possible to define the compression level at the initialization stage in libpq compression patch.
The difference is that in libpq compression patch the default compression level always equal to 1, independently of the chosen compression algorithm.
On Jan 4, 2021, at 11:04 AM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:
Libpq compression encountered some problems with memory consumption which required some extra config efforts.
On Jan 4, 2021, at 12:06 PM, Justin Pryzby <pryzby@telsasoft.com> wrote:
RAM use is not significantly different from zlib, except that zstd --long adds
more memory.Regarding ZSTD memory usage:
Recently I’ve made a couple of tests of libpq compression with different ZLIB/ZSTD compression levels which shown that compressing/decompressing ZSTD w/ high compression levels
require to allocate more virtual (Commited_AS) memory, which may be exploited by malicious clients:/messages/by-id/62527092-16BD-479F-B503-FA527AF3B0C2@yandex-team.ru
We can avoid high memory usage by limiting the max window size to 8MB. This should effectively disable the support of compression levels above 19:
/messages/by-id/6A45DFAA-1682-4EF2-B835-C5F46615EC49@yandex-team.ruSo maybe it is worthwhile to use similar restrictions in this patch.
I think there's a big difference between those two patches. In the libpq
case, the danger is that the client requests the server to compress the
data in a way that requires a lot of memory. I.e. the memory is consumed
on the server.
With this pg_dump patch, the compression is done by the pg_dump process,
not the server. So if the attacker configures the compression in a way
that requires a lot of memory, so what? He'll just allocate memory on
the client machine, where he could also just run a custom binary that
does a huge malloc().
So I don't think we need to worry about this too much.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 1/3/21 9:53 PM, Justin Pryzby wrote:
I rebased so the "typedef struct compression" patch is first and zstd on top of
that (say, in case someone wants to bikeshed about which compression algorithm
to support). And made a central struct with all the compression-specific info
to further isolate the compress-specific changes.
It has been a few months since there was a new patch and the current one
no longer applies, so marking Returned with Feedback.
Please resubmit to the next CF when you have a new patch.
Regards,
--
-David
david@pgmasters.net