-- 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.
SET timezone TO 'UTC';
CREATE TABLE metrics(
    time timestamptz NOT NULL,
    device text NOT NULL,
    sensor text NOT NULL,
    value float
) WITH (tsdb.hypertable,tsdb.orderby='time desc',tsdb.segmentby='device,sensor');
NOTICE:  using column "time" as partitioning column
-- create unordered chunks
SET timescaledb.enable_direct_compress_insert = true;
INSERT INTO metrics VALUES
('2025-01-01 00:00:00', 'd1', 'A', 10.0),
('2025-01-01 01:00:00', 'd1', 'A', 20.0),
('2025-01-01 02:00:00', 'd1', 'A', 15.0),
('2025-01-01 00:30:00', 'd1', 'B', 5.0),
('2025-01-01 01:30:00', 'd1', 'B', 25.0),
('2025-01-01 02:30:00', 'd1', 'B', 30.0),
('2025-01-01 00:00:00', 'd2', 'A', 10.0),
('2025-01-01 01:00:00', 'd2', 'A', 20.0),
('2025-01-01 02:00:00', 'd2', 'A', 15.0),
('2025-01-01 00:30:00', 'd2', 'C', 5.0),
('2025-01-01 01:30:00', 'd2', 'C', 25.0),
('2025-01-01 02:30:00', 'd2', 'C', 30.0);
INSERT INTO metrics VALUES
('2025-01-01 03:00:00', 'd1', 'A', 11.0),
('2025-01-01 01:00:00', 'd1', 'A', 21.0),
('2025-01-01 02:00:00', 'd1', 'A', 15.1),
('2025-01-01 03:30:00', 'd1', 'B', 5.1),
('2025-01-01 01:30:00', 'd1', 'B', 25.1),
('2025-01-01 02:30:00', 'd1', 'B', 31.0),
('2025-01-01 03:00:00', 'd2', 'A', 11.0),
('2025-01-01 01:00:00', 'd2', 'A', 21.0),
('2025-01-01 02:00:00', 'd2', 'A', 15.2),
('2025-01-01 03:30:00', 'd2', 'C', 5.3),
('2025-01-01 01:30:00', 'd2', 'C', 25.3),
('2025-01-01 02:30:00', 'd2', 'C', 32.0);
SELECT _timescaledb_functions.chunk_status_text(ch.status)
FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.chunk ch
WHERE h.id = ch.hypertable_id AND h.table_name = 'metrics';
   chunk_status_text    
------------------------
 {COMPRESSED,UNORDERED}

\set PREFIX 'EXPLAIN (buffers off, costs off, timing off, summary off)'
SET max_parallel_workers_per_gather = 0;
SET enable_bitmapscan=0;
SET enable_seqscan=0;
-- Can use SkipScan on unordered chunks if only segmentby columns are distinct
:PREFIX select distinct device, sensor from metrics order by 1,2;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select distinct device, sensor from metrics order by 1,2;
 device | sensor 
--------+--------
 d1     | A
 d1     | B
 d2     | A
 d2     | C

:PREFIX select distinct on(device) device from metrics order by device;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select distinct on(device) device from metrics order by device;
 device 
--------
 d1
 d2

:PREFIX select distinct on(device,sensor) device, sensor from metrics order by 1,2;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select distinct on(device,sensor) device, sensor from metrics order by 1,2;
 device | sensor 
--------+--------
 d1     | A
 d1     | B
 d2     | A
 d2     | C

:PREFIX select distinct on(device) device from metrics where sensor='A' order by device;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk
                     Index Cond: (sensor = 'A'::text)

select distinct on(device) device from metrics where sensor='A' order by device;
 device 
--------
 d1
 d2

:PREFIX select distinct on(device, sensor) device, sensor from metrics where device = 'd2' order by device, sensor;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk
                     Index Cond: (device = 'd2'::text)

select distinct on(device, sensor) device, sensor from metrics where device = 'd2' order by device, sensor;
 device | sensor 
--------+--------
 d2     | A
 d2     | C

:PREFIX select distinct on(device, sensor) device, sensor from metrics where value > 6 order by 1,2;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               Vectorized Filter: (value > '6'::double precision)
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select distinct on(device, sensor) device, sensor from metrics where value > 6 order by 1,2;
 device | sensor 
--------+--------
 d1     | A
 d1     | B
 d2     | A
 d2     | C

:PREFIX select distinct device, sensor from metrics where time > '2025-01-01 01:00:00' order by 1,2;
--- QUERY PLAN ---
 Unique
   ->  Custom Scan (SkipScan) on _hyper_1_1_chunk
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               Vectorized Filter: ("time" > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk
                     Index Cond: (_ts_meta_max_1 > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)

select distinct device, sensor from metrics where time > '2025-01-01 01:00:00' order by 1,2;
 device | sensor 
--------+--------
 d1     | A
 d1     | B
 d2     | A
 d2     | C

-- Can use compressed sort on unordered chunks when we need aggregation results ordered by segmentby columns
:PREFIX select device, sensor from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 Group
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor from metrics group by device, sensor order by 1,2;
 device | sensor 
--------+--------
 d1     | A
 d1     | B
 d2     | A
 d2     | C

:PREFIX select device, sensor, count(*), max(sensor) from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, count(*), max(sensor) from metrics group by device, sensor order by 1,2;
 device | sensor | count | max 
--------+--------+-------+-----
 d1     | A      |     6 | A
 d1     | B      |     6 | B
 d2     | A      |     6 | A
 d2     | C      |     6 | C

:PREFIX select device, sensor, count(*) from metrics where value > length(sensor) group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         Filter: (value > (length(sensor))::double precision)
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, count(*) from metrics where value > length(sensor) group by device, sensor order by 1,2;
 device | sensor | count 
--------+--------+-------
 d1     | A      |     6
 d1     | B      |     6
 d2     | A      |     6
 d2     | C      |     6

:PREFIX select device, sensor, avg(value) from metrics where time > '2025-01-01 01:00:00' group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         Vectorized Filter: ("time" > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk
               Index Cond: (_ts_meta_max_1 > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)

select device, sensor, avg(value) from metrics where time > '2025-01-01 01:00:00' group by device, sensor order by 1,2;
 device | sensor |       avg        
--------+--------+------------------
 d1     | A      |             13.7
 d1     | B      |            23.24
 d2     | A      | 13.7333333333333
 d2     | C      |            23.52

-- Can use expressions on aggregates as it won't affect grouping/sorting
:PREFIX select device, sensor, max(sensor||'1'), min(time), max(time) + interval '1 day' from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, max(sensor||'1'), min(time), max(time) + interval '1 day' from metrics group by device, sensor order by 1,2;
 device | sensor | max |             min              |           ?column?           
--------+--------+-----+------------------------------+------------------------------
 d1     | A      | A1  | Wed Jan 01 00:00:00 2025 UTC | Thu Jan 02 03:00:00 2025 UTC
 d1     | B      | B1  | Wed Jan 01 00:30:00 2025 UTC | Thu Jan 02 03:30:00 2025 UTC
 d2     | A      | A1  | Wed Jan 01 00:00:00 2025 UTC | Thu Jan 02 03:00:00 2025 UTC
 d2     | C      | C1  | Wed Jan 01 00:30:00 2025 UTC | Thu Jan 02 03:30:00 2025 UTC

:PREFIX select device, sensor, avg(value+1), max(time) + make_interval(days => length(sensor)) from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, avg(value+1), max(time) + make_interval(days => length(sensor)) from metrics group by device, sensor order by 1,2;
 device | sensor |       avg        |           ?column?           
--------+--------+------------------+------------------------------
 d1     | A      |            16.35 | Thu Jan 02 03:00:00 2025 UTC
 d1     | B      |             21.2 | Thu Jan 02 03:30:00 2025 UTC
 d2     | A      | 16.3666666666667 | Thu Jan 02 03:00:00 2025 UTC
 d2     | C      | 21.4333333333333 | Thu Jan 02 03:30:00 2025 UTC

-- Can use Having as it filters on aggregate outputs without changing sort
:PREFIX select device, sensor, count(*) from metrics group by device, sensor having count(*) > length(sensor) order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   Filter: (count(*) > length(_hyper_1_1_chunk.sensor))
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, count(*) from metrics group by device, sensor having count(*) > length(sensor) order by 1,2;
 device | sensor | count 
--------+--------+-------
 d1     | A      |     6
 d1     | B      |     6
 d2     | A      |     6
 d2     | C      |     6

-- Cannot use compressed sort on non-var keys
:PREFIX select device, sensor||'1', min(time), max(time) from metrics group by device, sensor||'1' order by 1,2;
--- QUERY PLAN ---
 Sort
   Sort Key: _hyper_1_1_chunk.device, ((_hyper_1_1_chunk.sensor || '1'::text))
   ->  HashAggregate
         Group Key: _hyper_1_1_chunk.device, (_hyper_1_1_chunk.sensor || '1'::text)
         ->  Result
               ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
                     ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor||'1', min(time), max(time) from metrics group by device, sensor||'1' order by 1,2;
 device | ?column? |             min              |             max              
--------+----------+------------------------------+------------------------------
 d1     | A1       | Wed Jan 01 00:00:00 2025 UTC | Wed Jan 01 03:00:00 2025 UTC
 d1     | B1       | Wed Jan 01 00:30:00 2025 UTC | Wed Jan 01 03:30:00 2025 UTC
 d2     | A1       | Wed Jan 01 00:00:00 2025 UTC | Wed Jan 01 03:00:00 2025 UTC
 d2     | C1       | Wed Jan 01 00:30:00 2025 UTC | Wed Jan 01 03:30:00 2025 UTC

-- Columnar Index Scan produces unsorted output currently,
-- so Columnar Index Scan eligible queries will not benefit from unordered sort, yet
:PREFIX select device, sensor, min(time) from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, min(time) from metrics group by device, sensor order by 1,2;
 device | sensor |             min              
--------+--------+------------------------------
 d1     | A      | Wed Jan 01 00:00:00 2025 UTC
 d1     | B      | Wed Jan 01 00:30:00 2025 UTC
 d2     | A      | Wed Jan 01 00:00:00 2025 UTC
 d2     | C      | Wed Jan 01 00:30:00 2025 UTC

:PREFIX select device, sensor, first(time,time) from metrics group by device, sensor order by 1,2;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor
   ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
         ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, first(time,time) from metrics group by device, sensor order by 1,2;
 device | sensor |            first             
--------+--------+------------------------------
 d1     | A      | Wed Jan 01 00:00:00 2025 UTC
 d1     | B      | Wed Jan 01 00:30:00 2025 UTC
 d2     | A      | Wed Jan 01 00:00:00 2025 UTC
 d2     | C      | Wed Jan 01 00:30:00 2025 UTC

-- Cannot use compressed sort on unordered chunk when sort keys contain non-segmentby keys
:PREFIX select device, sensor, time from metrics group by device, sensor, time order by 1,2, time DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor, _hyper_1_1_chunk."time" DESC
   ->  HashAggregate
         Group Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk.sensor, _hyper_1_1_chunk."time"
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select device, sensor, time from metrics group by device, sensor, time order by 1,2, time DESC;
 device | sensor |             time             
--------+--------+------------------------------
 d1     | A      | Wed Jan 01 03:00:00 2025 UTC
 d1     | A      | Wed Jan 01 02:00:00 2025 UTC
 d1     | A      | Wed Jan 01 01:00:00 2025 UTC
 d1     | A      | Wed Jan 01 00:00:00 2025 UTC
 d1     | B      | Wed Jan 01 03:30:00 2025 UTC
 d1     | B      | Wed Jan 01 02:30:00 2025 UTC
 d1     | B      | Wed Jan 01 01:30:00 2025 UTC
 d1     | B      | Wed Jan 01 00:30:00 2025 UTC
 d2     | A      | Wed Jan 01 03:00:00 2025 UTC
 d2     | A      | Wed Jan 01 02:00:00 2025 UTC
 d2     | A      | Wed Jan 01 01:00:00 2025 UTC
 d2     | A      | Wed Jan 01 00:00:00 2025 UTC
 d2     | C      | Wed Jan 01 03:30:00 2025 UTC
 d2     | C      | Wed Jan 01 02:30:00 2025 UTC
 d2     | C      | Wed Jan 01 01:30:00 2025 UTC
 d2     | C      | Wed Jan 01 00:30:00 2025 UTC

:PREFIX select distinct on (device) device, time from metrics order by device, time DESC;
--- QUERY PLAN ---
 Unique
   ->  Sort
         Sort Key: _hyper_1_1_chunk.device, _hyper_1_1_chunk."time" DESC
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk

select distinct on (device) device, time from metrics order by device, time DESC;
 device |             time             
--------+------------------------------
 d1     | Wed Jan 01 03:30:00 2025 UTC
 d2     | Wed Jan 01 03:30:00 2025 UTC

:PREFIX select time, avg(value) from metrics where device = 'd1' and sensor='A' group by time order by time DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: _hyper_1_1_chunk."time" DESC
   ->  HashAggregate
         Group Key: _hyper_1_1_chunk."time"
         ->  Custom Scan (ColumnarScan) on _hyper_1_1_chunk
               ->  Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_min_1__ts_m_idx on compress_hyper_2_2_chunk
                     Index Cond: ((device = 'd1'::text) AND (sensor = 'A'::text))

select time, avg(value) from metrics  where device = 'd1' and sensor='A' group by time order by time DESC;
             time             |  avg  
------------------------------+-------
 Wed Jan 01 03:00:00 2025 UTC |    11
 Wed Jan 01 02:00:00 2025 UTC | 15.05
 Wed Jan 01 01:00:00 2025 UTC |  20.5
 Wed Jan 01 00:00:00 2025 UTC |    10

-- Cannot use compressed sort on unordered chunk when columnstore doesn't have segmentby keys
CREATE TABLE metrics2(
    time timestamptz NOT NULL,
    device text NOT NULL,
    sensor text NOT NULL,
    value float
) WITH (tsdb.hypertable,tsdb.orderby='time desc');
NOTICE:  using column "time" as partitioning column
-- create unordered chunks
INSERT INTO metrics2 VALUES
('2025-01-01 00:00:00', 'd1', 'A', 10.0),
('2025-01-01 01:00:00', 'd1', 'A', 20.0),
('2025-01-01 02:00:00', 'd1', 'A', 15.0),
('2025-01-01 00:30:00', 'd1', 'B', 5.0),
('2025-01-01 01:30:00', 'd1', 'B', 25.0),
('2025-01-01 02:30:00', 'd1', 'B', 30.0),
('2025-01-01 00:00:00', 'd2', 'A', 10.0),
('2025-01-01 01:00:00', 'd2', 'A', 20.0),
('2025-01-01 02:00:00', 'd2', 'A', 15.0),
('2025-01-01 00:30:00', 'd2', 'C', 5.0),
('2025-01-01 01:30:00', 'd2', 'C', 25.0),
('2025-01-01 02:30:00', 'd2', 'C', 30.0);
SELECT _timescaledb_functions.chunk_status_text(ch.status)
FROM _timescaledb_catalog.hypertable h, _timescaledb_catalog.chunk ch
WHERE h.id = ch.hypertable_id AND h.table_name = 'metrics2';
   chunk_status_text    
------------------------
 {COMPRESSED,UNORDERED}

-- Cannot use compressed sort on unordered chunks for this columnstore as it's not segmented
:PREFIX select time, avg(value) from metrics2 where time > '2025-01-01 01:00:00' group by time order by time DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: _hyper_3_3_chunk."time" DESC
   ->  HashAggregate
         Group Key: _hyper_3_3_chunk."time"
         ->  Custom Scan (ColumnarScan) on _hyper_3_3_chunk
               Vectorized Filter: ("time" > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)
               ->  Index Scan using compress_hyper_4_4_chunk__ts_meta_min_1__ts_meta_max_1_idx on compress_hyper_4_4_chunk
                     Index Cond: (_ts_meta_max_1 > 'Wed Jan 01 01:00:00 2025 UTC'::timestamp with time zone)

select time, avg(value) from metrics2  where time > '2025-01-01 01:00:00' group by time order by time DESC;
             time             | avg 
------------------------------+-----
 Wed Jan 01 02:30:00 2025 UTC |  30
 Wed Jan 01 02:00:00 2025 UTC |  15
 Wed Jan 01 01:30:00 2025 UTC |  25

drop table metrics cascade;
drop table metrics2 cascade;
RESET timescaledb.enable_direct_compress_insert;
RESET max_parallel_workers_per_gather;
RESET enable_bitmapscan;
RESET enable_seqscan;
