Fix interaction of TOAST compression with expression indexes.

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
This commit is contained in:
Robert Haas 2021-03-25 19:55:32 -04:00
parent 71f4c8c6f7
commit 5db1fd7823
6 changed files with 54 additions and 5 deletions

View 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();

View 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)
{

View 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);
/*

View 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

View 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

View 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;