Advance input pointer when LZ4 compressing data
authorTomas Vondra <[email protected]>
Wed, 17 May 2023 14:49:31 +0000 (16:49 +0200)
committerTomas Vondra <[email protected]>
Wed, 17 May 2023 14:49:34 +0000 (16:49 +0200)
LZ4File_write() did not advance the input pointer on subsequent invocations of
LZ4F_compressUpdate(). As a result the generated compressed output would be a
compressed version of the same input chunk.

Tests failed to catch this error because the data would comfortably fit
within the default buffer size, as a single chunk. Tests have been added
to provide adequate coverage of multi-chunk compression.

WriteDataToArchiveLZ4() which is also using LZ4F_compressUpdate() did
not suffer from this omission.

Author: Georgios Kokolatos <[email protected]>
Reported-by: Michael Paquier <[email protected]>
Discussion: https://postgr.es/m/ZFhCyn4Gm2eu60rB%40paquier.xyz

src/bin/pg_dump/compress_io.h
src/bin/pg_dump/compress_lz4.c
src/bin/pg_dump/t/002_pg_dump.pl

index fd8752db0de391d9986a77411736b9cf854cc385..621e03a5c8583f38e505862f54a9267f8361c66f 100644 (file)
 
 #include "pg_backup_archiver.h"
 
-/* Default size used for IO buffers */
+/*
+ * Default size used for IO buffers
+ *
+ * When changing this value, it's necessary to check the relevant test cases
+ * still exercise all the branches. This applies especially if the value is
+ * increased, in which case the overflow buffer may not be needed.
+ */
 #define DEFAULT_IO_BUFFER_SIZE 4096
 
 extern char *supports_compression(const pg_compress_specification compression_spec);
index f97b7550d1c7edf18a80b637f43e4cc8e498ae46..b869780c0b4aa4133da6a03f9023430169d41af7 100644 (file)
@@ -588,6 +588,8 @@ LZ4Stream_write(const void *ptr, size_t size, CompressFileHandle *CFH)
                        errno = (errno) ? errno : ENOSPC;
                        return false;
                }
+
+               ptr = ((const char *) ptr) + chunk;
        }
 
        return true;
index 93e24d5145727c9027285c146703586e0bbc0dc6..d66f3b42ea42dcd793b29514f4a1fd1bafdd8921 100644 (file)
@@ -3108,6 +3108,52 @@ my %tests = (
                },
        },
 
+       'CREATE TABLE test_compression_method' => {
+               create_order => 110,
+               create_sql   => 'CREATE TABLE dump_test.test_compression_method (
+                                                  col1 text
+                                          );',
+               regexp => qr/^
+                       \QCREATE TABLE dump_test.test_compression_method (\E\n
+                       \s+\Qcol1 text\E\n
+                       \Q);\E
+                       /xm,
+               like => {
+                       %full_runs,
+                       %dump_test_schema_runs,
+                       section_pre_data     => 1,
+               },
+               unlike => {
+                       exclude_dump_test_schema => 1,
+                       only_dump_measurement    => 1,
+               },
+       },
+
+       # Insert enough data to surpass DEFAULT_IO_BUFFER_SIZE during
+       # (de)compression operations
+       'COPY test_compression_method' => {
+               create_order => 111,
+               create_sql   => 'INSERT INTO dump_test.test_compression_method (col1) '
+                 . 'SELECT string_agg(a::text, \'\') FROM generate_series(1,4096) a;',
+               regexp => qr/^
+                       \QCOPY dump_test.test_compression_method (col1) FROM stdin;\E
+                       \n(?:\d{15277}\n){1}\\\.\n
+                       /xm,
+               like => {
+                       %full_runs,
+                       data_only                => 1,
+                       section_data             => 1,
+                       only_dump_test_schema    => 1,
+                       test_schema_plus_large_objects    => 1,
+               },
+               unlike => {
+                       binary_upgrade           => 1,
+                       exclude_dump_test_schema => 1,
+                       schema_only              => 1,
+                       only_dump_measurement    => 1,
+               },
+       },
+
        'CREATE TABLE fk_reference_test_table' => {
                create_order => 21,
                create_sql   => 'CREATE TABLE dump_test.fk_reference_test_table (