Rebased on HEAD. 0005 forgot to update compression_1.out. Included changes to ./configure.ac and some other patches, but not Tomas's, since it'll make CFBOT get mad as soon as that's pushed.
-- Justin
>From bf1336d284792b29e30b42af2ec820bb0c256916 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 19:12:53 -0500 Subject: [PATCH v2 01/10] Add docs for default_toast_compression.. bbe0a81db69bd10bd166907c3701492a29aca294 --- doc/src/sgml/config.sgml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index ee4925d6d9..5cb851a5eb 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -8151,6 +8151,29 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; </listitem> </varlistentry> + <varlistentry id="guc-default-toast-compression" xreflabel="default_toast_compression"> + <term><varname>default_toast_compression</varname> (<type>string</type>) + <indexterm> + <primary><varname>default_toast_compression</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + This parameter specifies the default compression method to use + when compressing data in <acronym>TOAST</acronym> tables. + It applies only to variable-width data types. + It may be overriden by compression clauses in the + <command>CREATE</command> command, or changed after the relation is + created by <command>ALTER TABLE ... SET COMPRESSION</command>. + + The supported compression methods are <literal>pglz</literal> and + (if configured at the time <productname>PostgreSQL</productname> was + built) <literal>lz4</literal>. + The default is <literal>pglz</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-temp-tablespaces" xreflabel="temp_tablespaces"> <term><varname>temp_tablespaces</varname> (<type>string</type>) <indexterm> -- 2.17.0
>From 3184c87f129ec935fa46773728869c7c6af88025 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 21:52:28 -0500 Subject: [PATCH v2 02/10] doc: pg_dump --no-toast-compression --- doc/src/sgml/ref/pg_dump.sgml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index bcbb7a25fb..4a521186fb 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -931,6 +931,18 @@ PostgreSQL documentation </listitem> </varlistentry> + <varlistentry> + <term><option>--no-toast-compression</option></term> + <listitem> + <para> + Do not output commands to set <acronym>TOAST</acronym>compression + methods. + With this option, all objects will be created using whichever + compression method is the default during restore. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><option>--no-unlogged-table-data</option></term> <listitem> -- 2.17.0
>From 2212020a50478c66a830f15b23c3410e4d7bb501 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 20:23:40 -0500 Subject: [PATCH v2 03/10] Compression method is an char not an OID --- src/backend/commands/tablecmds.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9b2800bf5e..8e756e59d5 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7847,6 +7847,7 @@ SetIndexStorageProperties(Relation rel, Relation attrelation, index_close(indrel, lockmode); } } + /* * ALTER TABLE ALTER COLUMN SET STORAGE * @@ -15070,7 +15071,7 @@ ATExecSetCompression(AlteredTableInfo *tab, AttrNumber attnum; char *compression; char typstorage; - Oid cmoid; + char cmethod; Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; bool replace[Natts_pg_attribute]; @@ -15111,9 +15112,9 @@ ATExecSetCompression(AlteredTableInfo *tab, memset(replace, false, sizeof(replace)); /* get the attribute compression method. */ - cmoid = GetAttributeCompression(atttableform, compression); + cmethod = GetAttributeCompression(atttableform, compression); - atttableform->attcompression = cmoid; + atttableform->attcompression = cmethod; CatalogTupleUpdate(attrel, &tuple->t_self, tuple); InvokeObjectPostAlterHook(RelationRelationId, @@ -15123,7 +15124,7 @@ ATExecSetCompression(AlteredTableInfo *tab, ReleaseSysCache(tuple); /* apply changes to the index column as well */ - SetIndexStorageProperties(rel, attrel, attnum, cmoid, '\0', lockmode); + SetIndexStorageProperties(rel, attrel, attnum, cmethod, '\0', lockmode); table_close(attrel, RowExclusiveLock); /* make changes visible */ -- 2.17.0
>From d73643d7569ef74b9008d232f8c0ea33cb96ff3c Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 20:07:31 -0500 Subject: [PATCH v2 04/10] Remove duplicative macro --- src/include/access/toast_compression.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/include/access/toast_compression.h b/src/include/access/toast_compression.h index 514df0bed1..09af6e7810 100644 --- a/src/include/access/toast_compression.h +++ b/src/include/access/toast_compression.h @@ -50,8 +50,6 @@ typedef enum ToastCompressionId errdetail("This functionality requires the server to be built with lz4 support."), \ errhint("You need to rebuild PostgreSQL using --with-lz4."))) -#define IsValidCompression(cm) ((cm) != InvalidCompressionMethod) - #define IsStorageCompressible(storage) ((storage) != TYPSTORAGE_PLAIN && \ (storage) != TYPSTORAGE_EXTERNAL) -- 2.17.0
>From 79e6dd441a0624f611ee1f7ee037285feccda426 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 20:19:55 -0500 Subject: [PATCH v2 05/10] Error on invalid compression in CREATE and ALTER --- src/backend/commands/tablecmds.c | 10 +++++++--- src/test/regress/expected/compression.out | 6 ++++++ src/test/regress/expected/compression_1.out | 6 ++++++ src/test/regress/sql/compression.sql | 5 +++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 8e756e59d5..7efe27ac6c 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -17865,9 +17865,13 @@ GetAttributeCompression(Form_pg_attribute att, char *compression) /* fallback to default compression if it's not specified */ if (compression == NULL) - cmethod = GetDefaultToastCompression(); - else - cmethod = CompressionNameToMethod(compression); + return GetDefaultToastCompression(); + + cmethod = CompressionNameToMethod(compression); + if (!CompressionMethodIsValid(cmethod)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("invalid compression method \"%s\"", compression))); return cmethod; } diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 3de2886de0..501e0ead14 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -344,4 +344,10 @@ SELECT length(f1) FROM cmmove3; 10040 (2 rows) +CREATE TABLE t (a text COMPRESSION I_Do_Not_Exist_Compression); -- fails +ERROR: invalid compression method "i_do_not_exist_compression" +CREATE TABLE t (a text); +ALTER TABLE t ALTER a SET COMPRESSION I_Do_Not_Exist_Compression; -- fails +ERROR: invalid compression method "i_do_not_exist_compression" +DROP TABLE t; \set HIDE_TOAST_COMPRESSION true diff --git a/src/test/regress/expected/compression_1.out b/src/test/regress/expected/compression_1.out index 40aad81fa1..2a704e9b14 100644 --- a/src/test/regress/expected/compression_1.out +++ b/src/test/regress/expected/compression_1.out @@ -337,4 +337,10 @@ SELECT length(f1) FROM cmmove3; 10000 (1 row) +CREATE TABLE t (a text COMPRESSION I_Do_Not_Exist_Compression); -- fails +ERROR: invalid compression method "i_do_not_exist_compression" +CREATE TABLE t (a text); +ALTER TABLE t ALTER a SET COMPRESSION I_Do_Not_Exist_Compression; -- fails +ERROR: invalid compression method "i_do_not_exist_compression" +DROP TABLE t; \set HIDE_TOAST_COMPRESSION true diff --git a/src/test/regress/sql/compression.sql b/src/test/regress/sql/compression.sql index d97e26b6ee..afddd5a134 100644 --- a/src/test/regress/sql/compression.sql +++ b/src/test/regress/sql/compression.sql @@ -133,4 +133,9 @@ SELECT length(f1) FROM cmmove1; SELECT length(f1) FROM cmmove2; SELECT length(f1) FROM cmmove3; +CREATE TABLE t (a text COMPRESSION I_Do_Not_Exist_Compression); -- fails +CREATE TABLE t (a text); +ALTER TABLE t ALTER a SET COMPRESSION I_Do_Not_Exist_Compression; -- fails +DROP TABLE t; + \set HIDE_TOAST_COMPRESSION true -- 2.17.0
>From 2382b3fe3e609e99ee1742b872de5b4ae61b2ecc Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Sat, 20 Mar 2021 17:09:36 -0500 Subject: [PATCH v2 06/10] ./configure: Avoid warnings on Mac --with-lz4 Reported by Andrey Borodin and Tom Lane. >>> configure: WARNING: lz4.h: accepted by the compiler, rejected by the preprocessor! >>> configure: WARNING: lz4.h: proceeding with the compiler's result Possibly we could instead just *remove* the AC_CHECK_HEADERS? --- configure | 6 ++++++ configure.ac | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/configure b/configure index 6d34243dca..fa2b7692ca 100755 --- a/configure +++ b/configure @@ -13522,6 +13522,10 @@ fi fi if test "$with_lz4" = yes; then + ac_save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$LZ4_CFLAGS $CPPFLAGS" + + # Verify we have LZ4's header files for ac_header in lz4/lz4.h do : ac_fn_c_check_header_mongrel "$LINENO" "lz4/lz4.h" "ac_cv_header_lz4_lz4_h" "$ac_includes_default" @@ -13549,6 +13553,8 @@ fi done + + CPPFLAGS=$ac_save_CPPFLAGS fi if test "$with_gssapi" = yes ; then diff --git a/configure.ac b/configure.ac index e54e2fb632..bd92643a3b 100644 --- a/configure.ac +++ b/configure.ac @@ -1426,8 +1426,14 @@ Use --without-zlib to disable zlib support.])]) fi if test "$with_lz4" = yes; then + ac_save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$LZ4_CFLAGS $CPPFLAGS" + + # Verify we have LZ4's header files AC_CHECK_HEADERS(lz4/lz4.h, [], [AC_CHECK_HEADERS(lz4.h, [], [AC_MSG_ERROR([lz4.h header file is required for LZ4])])]) + + CPPFLAGS=$ac_save_CPPFLAGS fi if test "$with_gssapi" = yes ; then -- 2.17.0
>From 533a7281906e42db7329c6e44ca236094ab60ab9 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Sat, 20 Mar 2021 16:13:44 -0500 Subject: [PATCH v2 07/10] Commentary about slicing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My language, per gripe from Álvaro Herrera: | I updated the coverage script to use --with-lz4; results are updated. | While eyeballing the results I noticed this bit in | lz4_decompress_datum_slice(): | | + /* slice decompression not supported prior to 1.8.3 */ | + if (LZ4_versionNumber() < 10803) | + return lz4_decompress_datum(value); | | which I read as returning the complete decompressed datum if slice | decompression is not supported. I thought that was a bug, but looking | at the caller I realize that this isn't really a problem, since it's | detoast_attr_slice's responsibility to slice the result further -- no | bug, it's just wasteful. I suggest to add comments to this effect, | perhaps as the attached (feel free to reword, I think mine is awkward.) --- src/backend/access/common/detoast.c | 3 +++ src/backend/access/common/toast_compression.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c index 2fef40c2e9..316f5715e1 100644 --- a/src/backend/access/common/detoast.c +++ b/src/backend/access/common/detoast.c @@ -497,6 +497,9 @@ toast_decompress_datum(struct varlena *attr) * Decompress the front of a compressed version of a varlena datum. * offset handling happens in detoast_attr_slice. * Here we just decompress a slice from the front. + * + * If slice decompression is not supported, the full datum is decompressed, and + * then sliced by detoast_attr_slice. */ static struct varlena * toast_decompress_datum_slice(struct varlena *attr, int32 slicelength) diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c index a6f8b79a9e..dd31a54b77 100644 --- a/src/backend/access/common/toast_compression.c +++ b/src/backend/access/common/toast_compression.c @@ -203,6 +203,9 @@ lz4_decompress_datum(const struct varlena *value) /* * Decompress part of a varlena that was compressed using LZ4. + * + * If slice decompression is not supported, the full datum is decompressed, and + * then sliced by detoast_attr_slice. */ struct varlena * lz4_decompress_datum_slice(const struct varlena *value, int32 slicelength) -- 2.17.0
>From adc6eb10f79d1edf3df0d241557b7d23106b7cc6 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Sat, 20 Mar 2021 17:25:46 -0500 Subject: [PATCH v2 08/10] specially handle SET default_toast_compression=lz4 --- src/backend/access/common/toast_compression.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c index dd31a54b77..1617c382c2 100644 --- a/src/backend/access/common/toast_compression.c +++ b/src/backend/access/common/toast_compression.c @@ -304,6 +304,11 @@ check_default_toast_compression(char **newval, void **extra, GucSource source) errmsg("compression method \"%s\" does not exist", *newval))); } + else if (strcmp(*newval, "lz4") == 0) + { + GUC_check_errdetail("This functionality requires the server to be built with lz4 support."); + GUC_check_errhint("You need to rebuild PostgreSQL using --with-lz4."); + } else { GUC_check_errdetail("Compression method \"%s\" does not exist.", -- 2.17.0
>From 232987c6b79de7cf635664c28fa0062498485146 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Fri, 19 Mar 2021 19:39:10 -0500 Subject: [PATCH v2 09/10] Alternately: WIP: Change default_toast_compression GUC to an enum.. ..since the final version of the TOAST patch doesn't require catalog access --- src/backend/access/common/toast_compression.c | 54 +------------------ src/backend/utils/misc/guc.c | 30 +++++++---- src/include/access/toast_compression.h | 10 ++-- src/test/regress/expected/compression.out | 4 +- 4 files changed, 28 insertions(+), 70 deletions(-) diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c index 1617c382c2..9d9ffc6f97 100644 --- a/src/backend/access/common/toast_compression.c +++ b/src/backend/access/common/toast_compression.c @@ -23,8 +23,8 @@ #include "fmgr.h" #include "utils/builtins.h" -/* Compile-time default */ -char *default_toast_compression = DEFAULT_TOAST_COMPRESSION; +/* GUC */ +int default_toast_compression = TOAST_PGLZ_COMPRESSION_ID; /* * Compress a varlena using PGLZ. @@ -269,53 +269,3 @@ toast_get_compression_id(struct varlena *attr) return cmid; } - -/* - * Validate a new value for the default_toast_compression GUC. - */ -bool -check_default_toast_compression(char **newval, void **extra, GucSource source) -{ - if (**newval == '\0') - { - GUC_check_errdetail("%s cannot be empty.", - "default_toast_compression"); - return false; - } - - if (strlen(*newval) >= NAMEDATALEN) - { - GUC_check_errdetail("%s is too long (maximum %d characters).", - "default_toast_compression", NAMEDATALEN - 1); - return false; - } - - if (!CompressionMethodIsValid(CompressionNameToMethod(*newval))) - { - /* - * When source == PGC_S_TEST, don't throw a hard error for a - * nonexistent compression method, only a NOTICE. See comments in - * guc.h. - */ - if (source == PGC_S_TEST) - { - ereport(NOTICE, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("compression method \"%s\" does not exist", - *newval))); - } - else if (strcmp(*newval, "lz4") == 0) - { - GUC_check_errdetail("This functionality requires the server to be built with lz4 support."); - GUC_check_errhint("You need to rebuild PostgreSQL using --with-lz4."); - } - else - { - GUC_check_errdetail("Compression method \"%s\" does not exist.", - *newval); - return false; - } - } - - return true; -} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4272cf4821..eb1b343c09 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -510,6 +510,14 @@ static struct config_enum_entry shared_memory_options[] = { {NULL, 0, false} }; +static struct config_enum_entry default_toast_compression_options[] = { + {"pglz", TOAST_PGLZ_COMPRESSION_ID, false}, +#ifdef USE_LZ4 + {"lz4", TOAST_LZ4_COMPRESSION_ID, false}, +#endif + {NULL, 0, false} +}; + /* * Options for enum values stored in other modules */ @@ -3953,17 +3961,6 @@ static struct config_string ConfigureNamesString[] = check_default_table_access_method, NULL, NULL }, - { - {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT, - gettext_noop("Sets the default compression for new columns."), - NULL, - GUC_IS_NAME - }, - &default_toast_compression, - DEFAULT_TOAST_COMPRESSION, - check_default_toast_compression, NULL, NULL - }, - { {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the default tablespace to create tables and indexes in."), @@ -4605,6 +4602,17 @@ static struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL }, + { + {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the default compression for new columns."), + NULL, + GUC_IS_NAME + }, + &default_toast_compression, + 0, /* pglz */ + default_toast_compression_options, NULL, NULL + }, + { {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the transaction isolation level of each new transaction."), diff --git a/src/include/access/toast_compression.h b/src/include/access/toast_compression.h index 09af6e7810..88a46e264d 100644 --- a/src/include/access/toast_compression.h +++ b/src/include/access/toast_compression.h @@ -16,10 +16,7 @@ #include "utils/guc.h" /* GUCs */ -extern char *default_toast_compression; - -/* default compression method if not specified. */ -#define DEFAULT_TOAST_COMPRESSION "pglz" +extern int default_toast_compression; /* * Built-in compression method-id. The toast compression header will store @@ -100,7 +97,10 @@ CompressionNameToMethod(char *compression) static inline char GetDefaultToastCompression(void) { - return CompressionNameToMethod(default_toast_compression); + /* See also guc.c */ + char *const enumtoname[] = {"pglz", "lz4"}; + Assert(default_toast_compression < lengthof(enumtoname)); + return CompressionNameToMethod(enumtoname[default_toast_compression]); } /* pglz compression/decompression routines */ diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 501e0ead14..5cc76cdcd6 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -232,10 +232,10 @@ DETAIL: pglz versus lz4 -- test default_toast_compression GUC SET default_toast_compression = ''; ERROR: invalid value for parameter "default_toast_compression": "" -DETAIL: default_toast_compression cannot be empty. +HINT: Available values: pglz, lz4. SET default_toast_compression = 'I do not exist compression'; ERROR: invalid value for parameter "default_toast_compression": "I do not exist compression" -DETAIL: Compression method "I do not exist compression" does not exist. +HINT: Available values: pglz, lz4. SET default_toast_compression = 'lz4'; DROP TABLE cmdata2; CREATE TABLE cmdata2 (f1 text); -- 2.17.0