timescaledb/tsl/test/sql/compression_hypertable.sql
Sven Klemm f57d584dd2 Make compression settings per chunk
This patch implements changes to the compressed hypertable to allow per
chunk configuration. To enable this the compressed hypertable can no
longer be in an inheritance tree as the schema of the compressed chunk
is determined by the compression settings. While this patch implements
all the underlying infrastructure changes, the restrictions for changing
compression settings remain intact and will be lifted in a followup patch.
2024-01-17 12:53:07 +01:00

300 lines
10 KiB
PL/PgSQL

-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
\ir include/rand_generator.sql
\c :TEST_DBNAME :ROLE_SUPERUSER
\ir include/compression_utils.sql
CREATE TYPE customtype;
CREATE OR REPLACE FUNCTION customtype_in(cstring) RETURNS customtype
AS :TSL_MODULE_PATHNAME, 'ts_compression_custom_type_in'
LANGUAGE C IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION customtype_out(customtype) RETURNS cstring
AS :TSL_MODULE_PATHNAME, 'ts_compression_custom_type_out'
LANGUAGE C IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION customtype_eq(customtype, customtype) RETURNS BOOL
AS :TSL_MODULE_PATHNAME, 'ts_compression_custom_type_eq'
LANGUAGE C IMMUTABLE STRICT;
-- for testing purposes we need a fixed length pass-by-ref type, and one whose
-- alignment is greater than it's size. This type serves both purposes.
CREATE TYPE customtype (
INPUT = customtype_in,
OUTPUT = customtype_out,
INTERNALLENGTH = 2,
ALIGNMENT = double,
STORAGE = plain
);
create operator = (
leftarg = customtype,
rightarg = customtype,
procedure = customtype_eq,
commutator = =
);
CREATE OPERATOR CLASS customtype_ops
DEFAULT
FOR TYPE customtype
USING hash AS OPERATOR 1 =;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE test1 ("Time" timestamptz, i integer, b bigint, t text);
SELECT table_name from create_hypertable('test1', 'Time', chunk_time_interval=> INTERVAL '1 day');
INSERT INTO test1 SELECT t, gen_rand_minstd(), gen_rand_minstd(), gen_rand_minstd()::text FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-28 1:00', '1 hour') t;
ALTER TABLE test1 set (timescaledb.compress, timescaledb.compress_segmentby = '', timescaledb.compress_orderby = '"Time" DESC');
\set QUERY 'SELECT * FROM test1'
\set QUERY_ORDER 'ORDER BY "Time"'
\set HYPERTABLE_NAME 'test1'
\ir include/compression_test_hypertable.sql
\set TYPE timestamptz
\set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
-- check stats
SELECT DISTINCT attname, attstattarget
FROM pg_attribute
WHERE attrelid = '_timescaledb_internal._compressed_hypertable_2'::REGCLASS
AND attnum > 0
ORDER BY attname;
SELECT DISTINCT attname, attstattarget
FROM pg_attribute
WHERE attrelid in (SELECT format('%I.%I', schema_name, table_name)::regclass FROM _timescaledb_catalog.chunk ch WHERE ch.hypertable_id = 2)
AND attnum > 0
ORDER BY attname;
-- Test that the GUC to disable bulk decompression works.
explain (analyze, verbose, timing off, costs off, summary off)
select * from _timescaledb_internal._hyper_1_10_chunk;
set timescaledb.enable_bulk_decompression to false;
explain (analyze, verbose, timing off, costs off, summary off)
select * from _timescaledb_internal._hyper_1_10_chunk;
reset timescaledb.enable_bulk_decompression;
TRUNCATE test1;
/* should be no data in table */
SELECT * FROM test1;
/* nor compressed table */
SELECT * FROM _timescaledb_internal._compressed_hypertable_2;
/* the compressed table should not have chunks */
SELECT count(*) FROM _timescaledb_catalog.chunk WHERE hypertable_id = 2;
--add test for altered hypertable
CREATE TABLE test2 ("Time" timestamptz, i integer, b bigint, t text);
SELECT table_name from create_hypertable('test2', 'Time', chunk_time_interval=> INTERVAL '1 day');
--create some chunks with the old column numbers
INSERT INTO test2 SELECT t, gen_rand_minstd(), gen_rand_minstd(), gen_rand_minstd()::text FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-04 1:00', '1 hour') t;
ALTER TABLE test2 DROP COLUMN b;
--add default a value
ALTER TABLE test2 ADD COLUMN c INT DEFAULT -15;
--add default NULL
ALTER TABLE test2 ADD COLUMN d INT;
--write to both old chunks and new chunks with different column #s
INSERT INTO test2 SELECT t, gen_rand_minstd(), gen_rand_minstd()::text, gen_rand_minstd(), gen_rand_minstd() FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-06 1:00', '1 hour') t;
ALTER TABLE test2 set (timescaledb.compress, timescaledb.compress_segmentby = '', timescaledb.compress_orderby = 'c, "Time" DESC');
\set QUERY 'SELECT * FROM test2'
\set QUERY_ORDER 'ORDER BY c,"Time"'
\set HYPERTABLE_NAME 'test2'
\ir include/compression_test_hypertable.sql
\set TYPE int
\set ORDER_BY_COL_NAME c
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
\set TYPE timestamptz
\set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL_MIN _ts_meta_min_2
\set SEGMENT_META_COL_MAX _ts_meta_max_2
\ir include/compression_test_hypertable_segment_meta.sql
--TEST4 create segments with > 1000 rows.
CREATE TABLE test4 (
timec TIMESTAMPTZ NOT NULL,
location TEXT NOT NULL,
location2 char(10) NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL
);
--we want all the data to go into 1 chunk. so use 1 year chunk interval
select create_hypertable( 'test4', 'timec', chunk_time_interval=> '1 year'::interval);
alter table test4 set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'timec');
insert into test4
select generate_series('2018-01-01 00:00'::timestamp, '2018-01-31 00:00'::timestamp, '1 day'), 'NYC', 'klick', 55, 75;
insert into test4
select generate_series('2018-02-01 00:00'::timestamp, '2018-02-14 00:00'::timestamp, '1 min'), 'POR', 'klick', 55, 75;
select hypertable_name, num_chunks
from timescaledb_information.hypertables
where hypertable_name like 'test4';
select location, count(*)
from test4
group by location ORDER BY location;
\set QUERY 'SELECT * FROM test4'
\set QUERY_ORDER 'ORDER BY timec'
\set HYPERTABLE_NAME 'test4'
\ir include/compression_test_hypertable.sql
\set TYPE TIMESTAMPTZ
\set ORDER_BY_COL_NAME timec
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
-- check stats with segmentby
SELECT DISTINCT attname, attstattarget
FROM pg_attribute
WHERE attrelid = '_timescaledb_internal._compressed_hypertable_6'::REGCLASS
AND attnum > 0
ORDER BY attname;
SELECT DISTINCT attname, attstattarget
FROM pg_attribute
WHERE attrelid in (SELECT format('%I.%I', schema_name, table_name)::regclass FROM _timescaledb_catalog.chunk ch WHERE ch.hypertable_id = 6)
AND attnum > 0
ORDER BY attname;
--add hypertable with order by a non by-val type with NULLs
CREATE TABLE test5 (
time TIMESTAMPTZ NOT NULL,
device_id TEXT NULL,
temperature DOUBLE PRECISION NULL
);
--we want all the data to go into 1 chunk. so use 1 year chunk interval
select create_hypertable( 'test5', 'time', chunk_time_interval=> '1 day'::interval);
alter table test5 set (timescaledb.compress, timescaledb.compress_orderby = 'device_id, time');
insert into test5
select generate_series('2018-01-01 00:00'::timestamp, '2018-01-10 00:00'::timestamp, '2 hour'), 'device_1', gen_rand_minstd();
insert into test5
select generate_series('2018-01-01 00:00'::timestamp, '2018-01-10 00:00'::timestamp, '2 hour'), 'device_2', gen_rand_minstd();
insert into test5
select generate_series('2018-01-01 00:00'::timestamp, '2018-01-10 00:00'::timestamp, '2 hour'), NULL, gen_rand_minstd();
\set QUERY 'SELECT * FROM test5'
\set QUERY_ORDER 'ORDER BY device_id, time'
\set HYPERTABLE_NAME 'test5'
\ir include/compression_test_hypertable.sql
\set TYPE TEXT
\set ORDER_BY_COL_NAME device_id
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
TRUNCATE test5;
SELECT * FROM test5;
-- test 6, test with custom type, and NULLs in the segmentby
CREATE table test6(
time INT NOT NULL,
device_id INT,
data customtype
);
SELECT create_hypertable('test6', 'time', chunk_time_interval=> 50);
ALTER TABLE test6 SET
(timescaledb.compress, timescaledb.compress_segmentby='device_id', timescaledb.compress_orderby = 'time DESC');
INSERT INTO test6 SELECT t, d, customtype_in((t + d)::TEXT::cstring)
FROM generate_series(1, 200) t, generate_series(1, 3) d;
INSERT INTO test6 SELECT t, NULL, customtype_in(t::TEXT::cstring)
FROM generate_series(1, 200) t;
\set QUERY 'SELECT * FROM test6'
\set QUERY_ORDER 'ORDER BY device_id, time'
\set HYPERTABLE_NAME 'test6'
\ir include/compression_test_hypertable.sql
\set TYPE INT
\set ORDER_BY_COL_NAME time
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
DROP TABLE test6;
-- test 7, compress misc types, and NULLs in dictionaries
CREATE TABLE test7(time INT, c1 DATE, c2 TIMESTAMP, c3 FLOAT, n TEXT);
SELECT create_hypertable('test7', 'time', chunk_time_interval=> 200);
ALTER TABLE test7 SET
(timescaledb.compress, timescaledb.compress_orderby = 'time DESC, c1 DESC');
INSERT INTO test7
SELECT t, d, '2019/07/07 01:00', gen_rand_minstd(), 'a'
FROM generate_series(1, 10) t,
generate_series('2019/02/01'::DATE, '2019/02/10', '1d') d;
INSERT INTO test7
SELECT t, d, '2019/07/07 01:30', gen_rand_minstd(), NULL
FROM generate_series(10, 20) t,
generate_series('2019/03/01'::DATE, '2019/03/10', '1d') d;
\set QUERY 'SELECT * FROM test7'
\set QUERY_ORDER 'ORDER BY time, c1'
\set HYPERTABLE_NAME 'test7'
\ir include/compression_test_hypertable.sql
\set TYPE INT
\set ORDER_BY_COL_NAME time
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql
DROP TABLE test7;
-- helper function: float -> pseudorandom float [0..1].
create or replace function mix(x float4) returns float4 as $$ select ((hashfloat4(x) / (pow(2., 31) - 1) + 1) / 2)::float4 $$ language sql;
-- test gorilla for float4 and deltadelta for int32 and other types
create table test8(ts date, id int, value float4, valueint2 int2, valuebool bool);
select create_hypertable('test8', 'ts');
alter table test8 set (timescaledb.compress,
timescaledb.compress_segmentby = 'id',
timescaledb.compress_orderby = 'ts desc')
;
insert into test8
select '2022-02-02 02:02:02+03'::timestamptz + interval '1 month' * mix(x),
mix(x + 1.) * 20,
mix(x + 2.) * 50,
mix(x + 3.) * 100,
mix(x + 4.) > 0.1
from generate_series(1, 10000) x(x)
;
select compress_chunk(x) from show_chunks('test8') x;
select distinct on (id) * from test8
order by id, ts desc, value
;
drop table test8;