Fix interaction of TOAST compression with expression indexes.
authorRobert Haas <[email protected]>
Thu, 25 Mar 2021 23:55:32 +0000 (19:55 -0400)
committerRobert Haas <[email protected]>
Thu, 25 Mar 2021 23:55:32 +0000 (19:55 -0400)
Before, trying to compress a value for insertion into an expression
index would crash.

Dilip Kumar, with some editing by me. Report by Jaime Casanova.

Discussion: http://postgr.es/m/CAJKUy5gcs0zGOp6JXU2mMVdthYhuQpFk=S3V8DOKT=LZC1L36Q@mail.gmail.com

src/backend/access/brin/brin_tuple.c
src/backend/access/common/indextuple.c
src/backend/catalog/index.c
src/test/regress/expected/compression.out
src/test/regress/expected/compression_1.out
src/test/regress/sql/compression.sql

index 8d03e609a389d913094d6d0a39cc5c0b43cb278e..32ffd9f9fc246ba5c4e3fd4a3482690f8b374408 100644 (file)
@@ -220,10 +220,12 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 
                /*
                 * If the BRIN summary and indexed attribute use the same data
-                * type, we can use the same compression method. Otherwise we
-                * have to use the default method.
+                * type and it has a valid compression method, we can use the
+                * same compression method. Otherwise we have to use the
+                * default method.
                 */
-               if (att->atttypid == atttype->type_id)
+               if (att->atttypid == atttype->type_id &&
+                   CompressionMethodIsValid(att->attcompression))
                    compression = att->attcompression;
                else
                    compression = GetDefaultToastCompression();
index 1f6b7b77d4e835564de0a5ad7fbf32fcd3046349..ae932691af84fedabd0205e36ef72762f9b8a3d5 100644 (file)
@@ -103,8 +103,19 @@ index_form_tuple(TupleDesc tupleDescriptor,
            (att->attstorage == TYPSTORAGE_EXTENDED ||
             att->attstorage == TYPSTORAGE_MAIN))
        {
-           Datum       cvalue = toast_compress_datum(untoasted_values[i],
-                                                     att->attcompression);
+           Datum   cvalue;
+           char    compression = att->attcompression;
+
+           /*
+            * If the compression method is not valid, use the default. We
+            * don't expect this to happen for regular index columns, which
+            * inherit the setting from the corresponding table column, but
+            * we do expect it to happen whenever an expression is indexed.
+            */
+           if (!CompressionMethodIsValid(compression))
+               compression = GetDefaultToastCompression();
+
+           cvalue = toast_compress_datum(untoasted_values[i], compression);
 
            if (DatumGetPointer(cvalue) != NULL)
            {
index af30840c0447b88f81290511e434ab5dbd5e2cea..a628b3281ce8e73dd82ccb8b713d24b344e4e482 100644 (file)
@@ -30,6 +30,7 @@
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/toast_compression.h"
 #include "access/transam.h"
 #include "access/visibilitymap.h"
 #include "access/xact.h"
@@ -379,6 +380,15 @@ ConstructTupleDescriptor(Relation heapRelation,
            to->attalign = typeTup->typalign;
            to->atttypmod = exprTypmod(indexkey);
 
+           /*
+            * For expression columns, set attcompression invalid, since
+            * there's no table column from which to copy the value. Whenever
+            * we actually need to compress a value, we'll use whatever the
+            * current value of default_compression_method is at that point
+            * in time.
+            */
+           to->attcompression = InvalidCompressionMethod;
+
            ReleaseSysCache(tuple);
 
            /*
index 566a1877eac68781b7b434d2ed4ef4baf88a3373..61e97cb33cec347e8e83a78cee3174b9eeac9b6a 100644 (file)
@@ -313,6 +313,12 @@ SELECT pg_column_compression(f1) FROM cmdata;
  lz4
 (2 rows)
 
+-- test expression index
+DROP TABLE cmdata2;
+CREATE TABLE cmdata2 (f1 TEXT COMPRESSION pglz, f2 TEXT COMPRESSION lz4);
+CREATE UNIQUE INDEX idx1 ON cmdata2 ((f1 || f2));
+INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::TEXT FROM
+generate_series(1, 50) g), VERSION());
 -- check data is ok
 SELECT length(f1) FROM cmdata;
  length 
index 399093341548c01e73d4b0da7b7628a2cb1db2e5..d03d6255ae378b5b94b548f49290403500ef6250 100644 (file)
@@ -309,6 +309,19 @@ SELECT pg_column_compression(f1) FROM cmdata;
  pglz
 (2 rows)
 
+-- test expression index
+DROP TABLE cmdata2;
+CREATE TABLE cmdata2 (f1 TEXT COMPRESSION pglz, f2 TEXT COMPRESSION lz4);
+ERROR:  unsupported LZ4 compression method
+DETAIL:  This functionality requires the server to be built with lz4 support.
+HINT:  You need to rebuild PostgreSQL using --with-lz4.
+CREATE UNIQUE INDEX idx1 ON cmdata2 ((f1 || f2));
+ERROR:  relation "cmdata2" does not exist
+INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::TEXT FROM
+generate_series(1, 50) g), VERSION());
+ERROR:  relation "cmdata2" does not exist
+LINE 1: INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::...
+                    ^
 -- check data is ok
 SELECT length(f1) FROM cmdata;
  length 
index 5e178be04a2cb0fd5c1a20cd4e3bd4e091a25798..76d1776d832988027cd453322d20bfd74ddcdcb3 100644 (file)
@@ -130,6 +130,13 @@ SELECT pg_column_compression(f1) FROM cmdata;
 VACUUM FULL cmdata;
 SELECT pg_column_compression(f1) FROM cmdata;
 
+-- test expression index
+DROP TABLE cmdata2;
+CREATE TABLE cmdata2 (f1 TEXT COMPRESSION pglz, f2 TEXT COMPRESSION lz4);
+CREATE UNIQUE INDEX idx1 ON cmdata2 ((f1 || f2));
+INSERT INTO cmdata2 VALUES((SELECT array_agg(md5(g::TEXT))::TEXT FROM
+generate_series(1, 50) g), VERSION());
+
 -- check data is ok
 SELECT length(f1) FROM cmdata;
 SELECT length(f1) FROM cmdata1;