diff --git a/src/ts_catalog/array_utils.c b/src/ts_catalog/array_utils.c index b082c3700..99d83e809 100644 --- a/src/ts_catalog/array_utils.c +++ b/src/ts_catalog/array_utils.c @@ -4,10 +4,14 @@ * LICENSE-APACHE for a copy of the license. */ #include +#include #include +#include #include #include +#include +#include #include #include "array_utils.h" @@ -29,6 +33,26 @@ ts_array_length(ArrayType *arr) return ARR_DIMS(arr)[0]; } +extern TSDLLEXPORT bool +ts_array_equal(ArrayType *left, ArrayType *right) +{ + /* Quick exit if both are NULL or point to same thing. */ + if (left == right) + return true; + + if (left == NULL || right == NULL) + return false; + + Assert(left != NULL && right != NULL && ARR_NDIM(left) == 1 && ARR_NDIM(right) == 1); + + Datum result = OidFunctionCall2Coll(F_ARRAY_EQ, + DEFAULT_COLLATION_OID, + PointerGetDatum(left), + PointerGetDatum(right)); + + return DatumGetBool(result); +} + extern TSDLLEXPORT bool ts_array_is_member(ArrayType *arr, const char *name) { diff --git a/src/ts_catalog/array_utils.h b/src/ts_catalog/array_utils.h index 5850f48f2..5aa8d62c4 100644 --- a/src/ts_catalog/array_utils.h +++ b/src/ts_catalog/array_utils.h @@ -18,6 +18,7 @@ */ extern TSDLLEXPORT int ts_array_length(ArrayType *arr); +extern TSDLLEXPORT bool ts_array_equal(ArrayType *left, ArrayType *right); extern TSDLLEXPORT bool ts_array_is_member(ArrayType *arr, const char *name); extern TSDLLEXPORT int ts_array_position(ArrayType *arr, const char *name); diff --git a/src/ts_catalog/compression_settings.c b/src/ts_catalog/compression_settings.c index 433647a93..3d11a6b2f 100644 --- a/src/ts_catalog/compression_settings.c +++ b/src/ts_catalog/compression_settings.c @@ -19,6 +19,15 @@ static ScanTupleResult compression_settings_tuple_update(TupleInfo *ti, void *da static HeapTuple compression_settings_formdata_make_tuple(const FormData_compression_settings *fd, TupleDesc desc); +bool +ts_compression_settings_equal(const CompressionSettings *left, const CompressionSettings *right) +{ + return ts_array_equal(left->fd.segmentby, right->fd.segmentby) && + ts_array_equal(left->fd.orderby, right->fd.orderby) && + ts_array_equal(left->fd.orderby_desc, right->fd.orderby_desc) && + ts_array_equal(left->fd.orderby_nullsfirst, right->fd.orderby_nullsfirst); +} + CompressionSettings * ts_compression_settings_materialize(Oid ht_relid, Oid dst_relid) { diff --git a/src/ts_catalog/compression_settings.h b/src/ts_catalog/compression_settings.h index 6e374891b..9fba01862 100644 --- a/src/ts_catalog/compression_settings.h +++ b/src/ts_catalog/compression_settings.h @@ -23,6 +23,8 @@ TSDLLEXPORT CompressionSettings *ts_compression_settings_create(Oid relid, Array TSDLLEXPORT CompressionSettings *ts_compression_settings_get(Oid relid); TSDLLEXPORT CompressionSettings *ts_compression_settings_materialize(Oid ht_relid, Oid dst_relid); TSDLLEXPORT bool ts_compression_settings_delete(Oid relid); +TSDLLEXPORT bool ts_compression_settings_equal(const CompressionSettings *left, + const CompressionSettings *right); TSDLLEXPORT int ts_compression_settings_update(CompressionSettings *settings); diff --git a/tsl/src/compression/api.c b/tsl/src/compression/api.c index a631c23f8..f20d2279b 100644 --- a/tsl/src/compression/api.c +++ b/tsl/src/compression/api.c @@ -385,6 +385,13 @@ find_chunk_to_merge_into(Hypertable *ht, Chunk *current_chunk) compressed_chunk_interval + current_chunk_interval > max_chunk_interval) return NULL; + /* Get reloid of the previous compressed chunk */ + Oid prev_comp_reloid = ts_chunk_get_relid(previous_chunk->fd.compressed_chunk_id, false); + CompressionSettings *prev_comp_settings = ts_compression_settings_get(prev_comp_reloid); + CompressionSettings *ht_comp_settings = ts_compression_settings_get(ht->main_table_relid); + if (!ts_compression_settings_equal(ht_comp_settings, prev_comp_settings)) + return NULL; + return previous_chunk; } diff --git a/tsl/test/expected/compression_merge.out b/tsl/test/expected/compression_merge.out index 54932c56d..8878e5b71 100644 --- a/tsl/test/expected/compression_merge.out +++ b/tsl/test/expected/compression_merge.out @@ -659,3 +659,132 @@ SELECT count(*) FROM test8 WHERE series_id = 1; 481 (1 row) +-- Verify rollup is prevented when compression settings differ +CREATE TABLE test9(time TIMESTAMPTZ NOT NULL, value DOUBLE PRECISION NOT NULL, series_id BIGINT NOT NULL); +SELECT create_hypertable('test9', 'time', chunk_time_interval => INTERVAL '1 h'); + create_hypertable +--------------------- + (17,public,test9,t) +(1 row) + +ALTER TABLE test9 set (timescaledb.compress, + timescaledb.compress_segmentby = 'series_id', + timescaledb.compress_orderby = 'time', + timescaledb.compress_chunk_time_interval = '1 day'); +-- create chunk and compress +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 00:00:00'::TIMESTAMPTZ, 1, 1; +SELECT compress_chunk(show_chunks('test9'), true); + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk +(1 row) + +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 01:00:00'::TIMESTAMPTZ, 1, 1; +-- should be 2 chunk before rollup +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 01:00:00 2020 PST + test9 | Wed Jan 01 01:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST +(2 rows) + +SELECT compress_chunk(show_chunks('test9'), true); +NOTICE: chunk "_hyper_17_320_chunk" is already compressed + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk + _timescaledb_internal._hyper_17_320_chunk +(2 rows) + +-- should be 1 chunk because of rollup +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST +(1 row) + +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 02:00:00'::TIMESTAMPTZ, 1, 1; +-- should be 2 chunks again +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST + test9 | Wed Jan 01 02:00:00 2020 PST | Wed Jan 01 03:00:00 2020 PST +(2 rows) + +ALTER TABLE test9 SET (timescaledb.compress_segmentby = ''); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); +NOTICE: chunk "_hyper_17_320_chunk" is already compressed + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk + _timescaledb_internal._hyper_17_323_chunk +(2 rows) + + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST + test9 | Wed Jan 01 02:00:00 2020 PST | Wed Jan 01 03:00:00 2020 PST +(2 rows) + +ROLLBACK; +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time DESC'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); +NOTICE: chunk "_hyper_17_320_chunk" is already compressed + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk + _timescaledb_internal._hyper_17_323_chunk +(2 rows) + + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST + test9 | Wed Jan 01 02:00:00 2020 PST | Wed Jan 01 03:00:00 2020 PST +(2 rows) + +ROLLBACK; +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time NULLS FIRST'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); +NOTICE: chunk "_hyper_17_320_chunk" is already compressed + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk + _timescaledb_internal._hyper_17_323_chunk +(2 rows) + + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 02:00:00 2020 PST + test9 | Wed Jan 01 02:00:00 2020 PST | Wed Jan 01 03:00:00 2020 PST +(2 rows) + +ROLLBACK; +-- reset back to original settings +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); +NOTICE: chunk "_hyper_17_320_chunk" is already compressed + compress_chunk +------------------------------------------- + _timescaledb_internal._hyper_17_320_chunk + _timescaledb_internal._hyper_17_320_chunk +(2 rows) + + -- should be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + hypertable_name | range_start | range_end +-----------------+------------------------------+------------------------------ + test9 | Wed Jan 01 00:00:00 2020 PST | Wed Jan 01 03:00:00 2020 PST +(1 row) + +ROLLBACK; diff --git a/tsl/test/sql/compression_merge.sql b/tsl/test/sql/compression_merge.sql index 6774eb87b..03e5739c5 100644 --- a/tsl/test/sql/compression_merge.sql +++ b/tsl/test/sql/compression_merge.sql @@ -233,3 +233,57 @@ SET enable_seqscan TO OFF; SET enable_bitmapscan TO ON; SELECT count(*) FROM test8 WHERE series_id = 1; + +-- Verify rollup is prevented when compression settings differ +CREATE TABLE test9(time TIMESTAMPTZ NOT NULL, value DOUBLE PRECISION NOT NULL, series_id BIGINT NOT NULL); +SELECT create_hypertable('test9', 'time', chunk_time_interval => INTERVAL '1 h'); + +ALTER TABLE test9 set (timescaledb.compress, + timescaledb.compress_segmentby = 'series_id', + timescaledb.compress_orderby = 'time', + timescaledb.compress_chunk_time_interval = '1 day'); + +-- create chunk and compress +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 00:00:00'::TIMESTAMPTZ, 1, 1; +SELECT compress_chunk(show_chunks('test9'), true); +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 01:00:00'::TIMESTAMPTZ, 1, 1; + +-- should be 2 chunk before rollup +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; +SELECT compress_chunk(show_chunks('test9'), true); +-- should be 1 chunk because of rollup +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + +INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 02:00:00'::TIMESTAMPTZ, 1, 1; +-- should be 2 chunks again +SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; + +ALTER TABLE test9 SET (timescaledb.compress_segmentby = ''); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; +ROLLBACK; + +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time DESC'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; +ROLLBACK; + +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time NULLS FIRST'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); + -- should not be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; +ROLLBACK; + +-- reset back to original settings +ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time'); +BEGIN; + SELECT compress_chunk(show_chunks('test9'), true); + -- should be rolled up + SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2; +ROLLBACK; +