From aaffc1d5a69e5242067dd7ce81e5096e3765ec85 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Fri, 22 Apr 2022 19:10:45 +0200 Subject: [PATCH] Set null vector for insert into compressed table As part of inserting into a compressed table, the tuple is materialized, which computes the data size for the tuple using `heap_compute_data_size`. When computing the data size of the tuple, columns that are null are not considered and are just ignored. Columns that are dropped are, however, not explicitly checked and instead the `heap_compute_data_size` rely on these columns being set to null. When reading tuples from a compressed table for insert, the null vector is cleared, meaning that it by default is non-null. Since columns that are dropped are not explicitly processed, they are expected to have a defined value, which they do not have, causing a crash when an attempt to dereference them are made. This commit fixes this by setting the null vector to all null, and the code after will overwrite the columns with proper null bits, except the dropped columns that will be considered null. Fixes #4251 --- tsl/src/compression/compression.c | 19 ++++++++--- tsl/test/expected/compression_insert-12.out | 35 +++++++++++++++++++++ tsl/test/expected/compression_insert-13.out | 35 +++++++++++++++++++++ tsl/test/expected/compression_insert-14.out | 35 +++++++++++++++++++++ tsl/test/sql/compression_insert.sql.in | 32 +++++++++++++++++++ 5 files changed, 151 insertions(+), 5 deletions(-) diff --git a/tsl/src/compression/compression.c b/tsl/src/compression/compression.c index 6806064f9..4146d2b3a 100644 --- a/tsl/src/compression/compression.c +++ b/tsl/src/compression/compression.c @@ -1594,16 +1594,25 @@ compress_singlerow(CompressSingleRowState *cr, TupleTableSlot *in_slot) ExecClearTuple(out_slot); + /* ExecClearTuple above will leave dropped columns as non-null, which will + * cause a segmentation fault in `heap_compute_data_size` since that + * function expects dropped columns to have the null bit set. Since the + * null bits are set below for all columns except */ + memset(out_slot->tts_isnull, + true, + sizeof(*out_slot->tts_isnull) * out_slot->tts_tupleDescriptor->natts); + invalues = in_slot->tts_values; out_values = out_slot->tts_values; out_isnull = out_slot->tts_isnull; - /* Possible optimization: - * Can we do a pass through compression without a full copy? - * full copy needed for multiple values. But we are dealing only with a single value, - * so just need the result of transformation after passing it through the compressor function - * This probably needs a bit of rewrte of the compression algorithm code + /* Possible optimization: Can we do a pass through compression without a + * full copy? full copy needed for multiple values. But we are dealing + * only with a single value, so just need the result of transformation + * after passing it through the compressor function This probably needs a + * bit of rewrite of the compression algorithm code */ + Assert(row_compressor->n_input_columns == in_slot->tts_tupleDescriptor->natts); for (int col = 0; col < row_compressor->n_input_columns; col++) { PerColumn *column = &row_compressor->per_column[col]; diff --git a/tsl/test/expected/compression_insert-12.out b/tsl/test/expected/compression_insert-12.out index 43215ae12..8177c14a3 100644 --- a/tsl/test/expected/compression_insert-12.out +++ b/tsl/test/expected/compression_insert-12.out @@ -938,3 +938,38 @@ FROM INSERT INTO :TABLENAME SELECT; ERROR: direct insert into internal compressed hypertable is not supported \set ON_ERROR_STOP 1 +-- Test that inserting into a compressed table works even when the +-- column has been dropped. +CREATE TABLE test4 ( + timestamp timestamp without time zone not null, + ident text not null, + one double precision, + two double precision +); +SELECT * FROM create_hypertable('test4', 'timestamp'); + hypertable_id | schema_name | table_name | created +---------------+-------------+------------+--------- + 20 | public | test4 | t +(1 row) + +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-10-14 17:50:16.207', '2' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-11-14 17:50:16.207', '1' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-12-14 17:50:16.207', '3' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-01-14 17:50:16.207', '4' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-02-14 17:50:16.207', '5' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-03-14 17:50:16.207', '6' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-04-14 17:50:16.207', '7' ); +ALTER TABLE test4 SET ( + timescaledb.compress, + timescaledb.compress_orderby = 'timestamp', + timescaledb.compress_segmentby = 'ident' +); +select count(compress_chunk(ch)) FROM show_chunks('test4') ch; + count +------- + 7 +(1 row) + +ALTER TABLE test4 DROP COLUMN two; +INSERT INTO test4 VALUES ('2021-10-14 17:50:16.207', '7', NULL); +INSERT INTO test4 (timestamp, ident) VALUES ('2021-10-14 17:50:16.207', '7'); diff --git a/tsl/test/expected/compression_insert-13.out b/tsl/test/expected/compression_insert-13.out index 2c9402158..90822a249 100644 --- a/tsl/test/expected/compression_insert-13.out +++ b/tsl/test/expected/compression_insert-13.out @@ -938,3 +938,38 @@ FROM INSERT INTO :TABLENAME SELECT; ERROR: direct insert into internal compressed hypertable is not supported \set ON_ERROR_STOP 1 +-- Test that inserting into a compressed table works even when the +-- column has been dropped. +CREATE TABLE test4 ( + timestamp timestamp without time zone not null, + ident text not null, + one double precision, + two double precision +); +SELECT * FROM create_hypertable('test4', 'timestamp'); + hypertable_id | schema_name | table_name | created +---------------+-------------+------------+--------- + 20 | public | test4 | t +(1 row) + +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-10-14 17:50:16.207', '2' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-11-14 17:50:16.207', '1' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-12-14 17:50:16.207', '3' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-01-14 17:50:16.207', '4' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-02-14 17:50:16.207', '5' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-03-14 17:50:16.207', '6' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-04-14 17:50:16.207', '7' ); +ALTER TABLE test4 SET ( + timescaledb.compress, + timescaledb.compress_orderby = 'timestamp', + timescaledb.compress_segmentby = 'ident' +); +select count(compress_chunk(ch)) FROM show_chunks('test4') ch; + count +------- + 7 +(1 row) + +ALTER TABLE test4 DROP COLUMN two; +INSERT INTO test4 VALUES ('2021-10-14 17:50:16.207', '7', NULL); +INSERT INTO test4 (timestamp, ident) VALUES ('2021-10-14 17:50:16.207', '7'); diff --git a/tsl/test/expected/compression_insert-14.out b/tsl/test/expected/compression_insert-14.out index 2c9402158..90822a249 100644 --- a/tsl/test/expected/compression_insert-14.out +++ b/tsl/test/expected/compression_insert-14.out @@ -938,3 +938,38 @@ FROM INSERT INTO :TABLENAME SELECT; ERROR: direct insert into internal compressed hypertable is not supported \set ON_ERROR_STOP 1 +-- Test that inserting into a compressed table works even when the +-- column has been dropped. +CREATE TABLE test4 ( + timestamp timestamp without time zone not null, + ident text not null, + one double precision, + two double precision +); +SELECT * FROM create_hypertable('test4', 'timestamp'); + hypertable_id | schema_name | table_name | created +---------------+-------------+------------+--------- + 20 | public | test4 | t +(1 row) + +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-10-14 17:50:16.207', '2' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-11-14 17:50:16.207', '1' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-12-14 17:50:16.207', '3' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-01-14 17:50:16.207', '4' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-02-14 17:50:16.207', '5' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-03-14 17:50:16.207', '6' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-04-14 17:50:16.207', '7' ); +ALTER TABLE test4 SET ( + timescaledb.compress, + timescaledb.compress_orderby = 'timestamp', + timescaledb.compress_segmentby = 'ident' +); +select count(compress_chunk(ch)) FROM show_chunks('test4') ch; + count +------- + 7 +(1 row) + +ALTER TABLE test4 DROP COLUMN two; +INSERT INTO test4 VALUES ('2021-10-14 17:50:16.207', '7', NULL); +INSERT INTO test4 (timestamp, ident) VALUES ('2021-10-14 17:50:16.207', '7'); diff --git a/tsl/test/sql/compression_insert.sql.in b/tsl/test/sql/compression_insert.sql.in index 7b9c56908..cb45dc9c6 100644 --- a/tsl/test/sql/compression_insert.sql.in +++ b/tsl/test/sql/compression_insert.sql.in @@ -612,3 +612,35 @@ FROM INSERT INTO :TABLENAME SELECT; \set ON_ERROR_STOP 1 +-- Test that inserting into a compressed table works even when the +-- column has been dropped. +CREATE TABLE test4 ( + timestamp timestamp without time zone not null, + ident text not null, + one double precision, + two double precision +); + +SELECT * FROM create_hypertable('test4', 'timestamp'); + +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-10-14 17:50:16.207', '2' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-11-14 17:50:16.207', '1' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2021-12-14 17:50:16.207', '3' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-01-14 17:50:16.207', '4' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-02-14 17:50:16.207', '5' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-03-14 17:50:16.207', '6' ); +INSERT INTO test4 ( timestamp, ident ) VALUES ( '2022-04-14 17:50:16.207', '7' ); + +ALTER TABLE test4 SET ( + timescaledb.compress, + timescaledb.compress_orderby = 'timestamp', + timescaledb.compress_segmentby = 'ident' +); + +select count(compress_chunk(ch)) FROM show_chunks('test4') ch; + +ALTER TABLE test4 DROP COLUMN two; + +INSERT INTO test4 VALUES ('2021-10-14 17:50:16.207', '7', NULL); +INSERT INTO test4 (timestamp, ident) VALUES ('2021-10-14 17:50:16.207', '7'); +