Skip to content

Commit

Permalink
Add tests for unique index creation with Hypercore TAM
Browse files Browse the repository at this point in the history
Unlike regular compressed tables, Hypercore TAM supports creating
unique indexes after compression is enabled. Add tests for this use
case.
  • Loading branch information
erimatnor committed Dec 11, 2024
1 parent ce6be27 commit 1be011a
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 0 deletions.
112 changes: 112 additions & 0 deletions tsl/test/expected/hypercore_index_btree.out
Original file line number Diff line number Diff line change
Expand Up @@ -987,3 +987,115 @@ select * from only_nulls_null;
---------+------+--------+----------+-------+------------
(0 rows)

--------------------------------------------------
-- Test unique index creation --
--------------------------------------------------
create table uniquetable (time timestamptz not null, value int);
select create_hypertable('uniquetable', 'time', create_default_indexes => false);
create_hypertable
--------------------------
(7,public,uniquetable,t)
(1 row)

insert into uniquetable values ('2024-01-01 01:00', 1), ('2024-01-01 02:00', 2);
select format('%I.%I', chunk_schema, chunk_name)::regclass as unique_chunk
from timescaledb_information.chunks
where format('%I.%I', hypertable_schema, hypertable_name)::regclass = 'uniquetable'::regclass
order by unique_chunk asc
limit 1 \gset
alter table uniquetable set (timescaledb.compress_orderby='time');
WARNING: there was some uncertainty picking the default segment by for the hypertable: You do not have any indexes on columns that can be used for segment_by and thus we are not using segment_by for compression. Please make sure you are not missing any indexes
NOTICE: default segment by for hypertable "uniquetable" is set to ""
-- Create a non-Hypercore TAM compressed chunk
select * from compress_chunk(:'unique_chunk');
compress_chunk
-----------------------------------------
_timescaledb_internal._hyper_7_19_chunk
(1 row)

-- Should still be a "heap" chunk
select c.relname, a.amname from pg_class c
inner join pg_am a ON (c.relam = a.oid)
where c.oid = :'unique_chunk'::regclass;
relname | amname
-------------------+--------
_hyper_7_19_chunk | heap
(1 row)

insert into uniquetable values ('2024-01-01 01:00', 3);
-- Unique index creation on compressed chunk not supported
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
ERROR: operation not supported on hypertables that have compression enabled
\set ON_ERROR_STOP 1
-- Convert the chunk to using Hypercore TAM
alter table :unique_chunk set access method hypercore;
-- Should now be a chunk using Hypercore TAM
select c.relname, a.amname from pg_class c
inner join pg_am a ON (c.relam = a.oid)
where c.oid = :'unique_chunk'::regclass;
relname | amname
-------------------+-----------
_hyper_7_19_chunk | hypercore
(1 row)

select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;
is_compressed_tid | time | value
-------------------+------------------------------+-------
t | Mon Jan 01 01:00:00 2024 PST | 1
f | Mon Jan 01 01:00:00 2024 PST | 3
t | Mon Jan 01 02:00:00 2024 PST | 2
(3 rows)

-- Unique index creation should work but fail on uniqueness check
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
ERROR: could not create unique index "_hyper_7_19_chunk_time_key"
DETAIL: Key ("time")=(Mon Jan 01 01:00:00 2024 PST) is duplicated.
\set ON_ERROR_STOP 1
-- Recompress to get all values in compressed format
select compress_chunk(:'unique_chunk');
compress_chunk
-----------------------------------------
_timescaledb_internal._hyper_7_19_chunk
(1 row)

-- Everything's compressed
select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;
is_compressed_tid | time | value
-------------------+------------------------------+-------
t | Mon Jan 01 01:00:00 2024 PST | 3
t | Mon Jan 01 01:00:00 2024 PST | 1
t | Mon Jan 01 02:00:00 2024 PST | 2
(3 rows)

-- Unique index creation should still fail
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
ERROR: could not create unique index "_hyper_7_19_chunk_time_key"
DETAIL: Key ("time")=(Mon Jan 01 01:00:00 2024 PST) is duplicated.
\set ON_ERROR_STOP 1
-- Delete the conflicting value and unique index creation should succeed
delete from uniquetable where value = 3;
select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;
is_compressed_tid | time | value
-------------------+------------------------------+-------
f | Mon Jan 01 01:00:00 2024 PST | 1
f | Mon Jan 01 02:00:00 2024 PST | 2
(2 rows)

create unique index time_key on uniquetable (time);
explain (costs off)
select * from uniquetable where time = '2024-01-01 01:00';
QUERY PLAN
-----------------------------------------------------------------------------------
Index Scan using _hyper_7_19_chunk_time_key on _hyper_7_19_chunk
Index Cond: ("time" = 'Mon Jan 01 01:00:00 2024 PST'::timestamp with time zone)
(2 rows)

select * from uniquetable where time = '2024-01-01 01:00';
time | value
------------------------------+-------
Mon Jan 01 01:00:00 2024 PST | 1
(1 row)

64 changes: 64 additions & 0 deletions tsl/test/sql/hypercore_index_btree.sql
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,67 @@ select * from nullvalues where only_nulls is null
except
select * from only_nulls_null;

--------------------------------------------------
-- Test unique index creation --
--------------------------------------------------
create table uniquetable (time timestamptz not null, value int);
select create_hypertable('uniquetable', 'time', create_default_indexes => false);
insert into uniquetable values ('2024-01-01 01:00', 1), ('2024-01-01 02:00', 2);

select format('%I.%I', chunk_schema, chunk_name)::regclass as unique_chunk
from timescaledb_information.chunks
where format('%I.%I', hypertable_schema, hypertable_name)::regclass = 'uniquetable'::regclass
order by unique_chunk asc
limit 1 \gset

alter table uniquetable set (timescaledb.compress_orderby='time');

-- Create a non-Hypercore TAM compressed chunk
select * from compress_chunk(:'unique_chunk');

-- Should still be a "heap" chunk
select c.relname, a.amname from pg_class c
inner join pg_am a ON (c.relam = a.oid)
where c.oid = :'unique_chunk'::regclass;

insert into uniquetable values ('2024-01-01 01:00', 3);

-- Unique index creation on compressed chunk not supported
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
\set ON_ERROR_STOP 1

-- Convert the chunk to using Hypercore TAM
alter table :unique_chunk set access method hypercore;

-- Should now be a chunk using Hypercore TAM
select c.relname, a.amname from pg_class c
inner join pg_am a ON (c.relam = a.oid)
where c.oid = :'unique_chunk'::regclass;

select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;

-- Unique index creation should work but fail on uniqueness check
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
\set ON_ERROR_STOP 1

-- Recompress to get all values in compressed format
select compress_chunk(:'unique_chunk');

-- Everything's compressed
select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;

-- Unique index creation should still fail
\set ON_ERROR_STOP 0
create unique index time_key on uniquetable (time);
\set ON_ERROR_STOP 1

-- Delete the conflicting value and unique index creation should succeed
delete from uniquetable where value = 3;
select _timescaledb_debug.is_compressed_tid(ctid), * from :unique_chunk order by time;
create unique index time_key on uniquetable (time);

explain (costs off)
select * from uniquetable where time = '2024-01-01 01:00';
select * from uniquetable where time = '2024-01-01 01:00';

0 comments on commit 1be011a

Please sign in to comment.