-- 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.
-- Some primitive tests that show cost of DecompressChunk node so that we can
-- monitor the changes.
create table costtab(ts int, s text, c text, ti text, fi float);
select create_hypertable('costtab', 'ts');
  create_hypertable   
----------------------
 (1,public,costtab,t)

alter table costtab set (timescaledb.compress, timescaledb.compress_segmentby = 's',
    timescaledb.compress_orderby = 'ts');
insert into costtab select ts, ts % 10, ts::text, ts::text, ts::float from generate_series(1, 10000) ts;
create index on costtab(ti);
create index on costtab(fi);
select count(compress_chunk(x)) from show_chunks('costtab') x;
 count 
-------
     1

vacuum freeze analyze costtab;
explain (buffers off) select * from costtab;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=108)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=142)

explain (buffers off) select * from costtab where s = '1';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.12..11.12 rows=1000 width=108)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.12 rows=1 width=142)
         Filter: (s = '1'::text)

explain (buffers off) select * from costtab where c = '100';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=108)
   Vectorized Filter: (c = '100'::text)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=142)

explain (buffers off) select * from costtab where ti = '200';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.38..31.12 rows=3000 width=108)
   Vectorized Filter: (ti = '200'::text)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.12 rows=3 width=142)
         Filter: _timescaledb_functions.bloom1_contains(regress-test-bloom_ti, '200'::text)

explain (buffers off) select * from costtab where fi = 200;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.15 rows=10000 width=108)
   Vectorized Filter: (fi = '200'::double precision)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.15 rows=10 width=142)
         Filter: ((_ts_meta_v2_min_fi <= '200'::double precision) AND (_ts_meta_v2_max_fi >= '200'::double precision))

explain (buffers off) select ts from costtab;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=4)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=44)

explain (buffers off) select ts from costtab where s = '1';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.12..11.12 rows=1000 width=4)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.12 rows=1 width=46)
         Filter: (s = '1'::text)

explain (buffers off) select ts from costtab where c = '100';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=4)
   Vectorized Filter: (c = '100'::text)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=76)

explain (buffers off) select ts, s from costtab;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=36)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=46)

explain (buffers off) select ts, s from costtab where s = '1';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.12..11.12 rows=1000 width=36)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.12 rows=1 width=46)
         Filter: (s = '1'::text)

explain (buffers off) select ts, s from costtab where c = '100';
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=0.11..101.10 rows=10000 width=36)
   Vectorized Filter: (c = '100'::text)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.10 rows=10 width=78)

explain (buffers off) select * from costtab where ts = 5000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.15..11.15 rows=1000 width=108)
   Vectorized Filter: (ts = 5000)
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.15 rows=1 width=142)
         Filter: ((_ts_meta_min_1 <= 5000) AND (_ts_meta_max_1 >= 5000))

explain (buffers off) select * from costtab where fi = 200 and ts = 5000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.20..11.20 rows=1000 width=108)
   Vectorized Filter: ((fi = '200'::double precision) AND (ts = 5000))
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.20 rows=1 width=142)
         Filter: ((_ts_meta_v2_min_fi <= '200'::double precision) AND (_ts_meta_v2_max_fi >= '200'::double precision) AND (_ts_meta_min_1 <= 5000) AND (_ts_meta_max_1 >= 5000))

explain (buffers off) select * from costtab where s = '1' or (fi = 200 and ts = 5000);
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_1_1_chunk  (cost=1.23..11.22 rows=1000 width=108)
   Filter: ((s = '1'::text) OR ((fi = '200'::double precision) AND (ts = 5000)))
   ->  Seq Scan on compress_hyper_2_2_chunk  (cost=0.00..1.23 rows=1 width=142)
         Filter: ((s = '1'::text) OR ((_ts_meta_v2_min_fi <= '200'::double precision) AND (_ts_meta_v2_max_fi >= '200'::double precision) AND (_ts_meta_min_1 <= 5000) AND (_ts_meta_max_1 >= 5000)))

-- Test estimation of compressed batch size using the _ts_meta_count stats.
create table estimate_count(time timestamptz, device int, value float);
select create_hypertable('estimate_count','time');
      create_hypertable      
-----------------------------
 (3,public,estimate_count,t)

alter table estimate_count
  set (timescaledb.compress,
       timescaledb.compress_segmentby = 'device',
       timescaledb.compress_orderby   = 'time');
-- same batch sizes
insert into estimate_count
select t, d, 1
from generate_series('2025-01-01'::timestamptz,'2025-01-03','15 min') t,
  generate_series(1, 1000) d
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     2

vacuum analyze estimate_count;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Append  (cost=0.04..2974.00 rows=193000 width=20) (actual rows=193000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_3_chunk  (cost=0.04..679.00 rows=64000 width=20) (actual rows=64000.00 loops=1)
         ->  Seq Scan on compress_hyper_4_5_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_4_chunk  (cost=0.04..1330.00 rows=129000 width=20) (actual rows=129000.00 loops=1)
         ->  Seq Scan on compress_hyper_4_6_chunk  (cost=0.00..40.00 rows=1000 width=88) (actual rows=1000.00 loops=1)

-- different batch sizes
truncate estimate_count;
insert into estimate_count
select t, d, 2
from generate_series(1, 1000) d,
    lateral generate_series('2025-01-01'::timestamptz,'2025-01-03',
        interval '15 min' * (d % 10 + 1)) t
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     2

vacuum analyze estimate_count;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Append  (cost=0.04..934.50 rows=57100 width=20) (actual rows=57100.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_7_chunk  (cost=0.04..230.00 rows=19100 width=20) (actual rows=19100.00 loops=1)
         ->  Seq Scan on compress_hyper_4_9_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_8_chunk  (cost=0.04..419.00 rows=38000 width=20) (actual rows=38000.00 loops=1)
         ->  Seq Scan on compress_hyper_4_10_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)

-- more different batch sizes
truncate estimate_count;
insert into estimate_count
select t, d, 2
from generate_series(1, 1000) d,
    lateral generate_series('2025-01-01'::timestamptz,'2025-01-03',
        interval '15 min' + interval '1 minute' * d) t
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     2

vacuum analyze estimate_count;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Append  (cost=0.04..265.21 rows=12548 width=20) (actual rows=12559.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_11_chunk  (cost=0.04..83.50 rows=4550 width=20) (actual rows=4553.00 loops=1)
         ->  Seq Scan on compress_hyper_4_13_chunk  (cost=0.00..38.00 rows=1000 width=88) (actual rows=1000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_12_chunk  (cost=0.04..118.97 rows=7997 width=20) (actual rows=8006.00 loops=1)
         ->  Seq Scan on compress_hyper_4_14_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)

-- more different + one very frequent
truncate estimate_count;
insert into estimate_count
select t, d, 2
from generate_series(1, 1000) d,
    lateral generate_series('2025-01-01'::timestamptz,'2025-01-03',
        case when d % 2 = 0 then interval '10 min'
            else interval '15 min' + interval '1 minute' * d end) t
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     2

vacuum analyze estimate_count;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Append  (cost=0.04..2340.10 rows=150807 width=20) (actual rows=150830.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_15_chunk  (cost=0.04..541.81 rows=50281 width=20) (actual rows=50287.00 loops=1)
         ->  Seq Scan on compress_hyper_4_17_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_16_chunk  (cost=0.04..1044.26 rows=100526 width=20) (actual rows=100543.00 loops=1)
         ->  Seq Scan on compress_hyper_4_18_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)

-- single row. Postgres generates all-zero entry in pg_statistics in this case,
-- but we want to avoid zero row counts.
truncate estimate_count;
insert into estimate_count
select '2025-01-01', 1, 2
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     1

vacuum analyze estimate_count;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_3_19_chunk  (cost=1.01..11.01 rows=1000 width=20) (actual rows=1.00 loops=1)
   ->  Seq Scan on compress_hyper_4_20_chunk  (cost=0.00..1.01 rows=1 width=88) (actual rows=1.00 loops=1)

-- no statistics
truncate estimate_count;
vacuum analyze estimate_count;
insert into estimate_count
select t, d, 1
from generate_series('2025-01-01'::timestamptz,'2025-01-03','15 min') t,
  generate_series(1, 1000) d
;
select count(compress_chunk(c)) from show_chunks('estimate_count') c;
 count 
-------
     2

vacuum analyze estimate_count;
\c :TEST_DBNAME :ROLE_SUPERUSER
with hypertables as (
    select unnest(array[compressed_hypertable_id, id])
    from _timescaledb_catalog.hypertable
    where (schema_name || '.' || table_name)::regclass = 'estimate_count'::regclass)
, chunks as (
    select (schema_name || '.' || table_name)::regclass
    from _timescaledb_catalog.chunk
    where hypertable_id in (select * from hypertables)
)
delete from pg_statistic
where starelid in (select * from chunks)
;
explain (analyze, timing off, summary off, buffers off) select * from estimate_count;
--- QUERY PLAN ---
 Append  (cost=0.04..30079.00 rows=2000000 width=20) (actual rows=193000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_21_chunk  (cost=0.04..10039.00 rows=1000000 width=20) (actual rows=64000.00 loops=1)
         ->  Seq Scan on compress_hyper_4_23_chunk  (cost=0.00..39.00 rows=1000 width=88) (actual rows=1000.00 loops=1)
   ->  Custom Scan (ColumnarScan) on _hyper_3_22_chunk  (cost=0.04..10040.00 rows=1000000 width=20) (actual rows=129000.00 loops=1)
         ->  Seq Scan on compress_hyper_4_24_chunk  (cost=0.00..40.00 rows=1000 width=88) (actual rows=1000.00 loops=1)

-- Test a high-cardinality orderby column
create table highcard(ts int) with (tsdb.hypertable, tsdb.partition_column = 'ts',
    tsdb.compress_orderby = 'ts', tsdb.chunk_interval = 10000000);
insert into highcard select generate_series(1, 1000000);
select count(compress_chunk(x)) from show_chunks('highcard') x;
 count 
-------
     1

vacuum freeze analyze highcard;
explain (buffers off, analyze, timing off, summary off)
select * from highcard where ts > 200000 and ts < 300000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_5_25_chunk  (cost=0.43..1005.58 rows=99000 width=4) (actual rows=99999.00 loops=1)
   Vectorized Filter: ((ts > 200000) AND (ts < 300000))
   Rows Removed by Filter: 1
   ->  Index Scan using compress_hyper_6_26_chunk__ts_meta_min_1__ts_meta_max_1_idx on compress_hyper_6_26_chunk  (cost=0.28..15.58 rows=99 width=44) (actual rows=100.00 loops=1)
         Index Cond: ((_ts_meta_min_1 < 300000) AND (_ts_meta_max_1 > 200000))

explain (buffers off, analyze, timing off, summary off)
select * from highcard where ts = 500000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_5_25_chunk  (cost=18.70..28.70 rows=1000 width=4) (actual rows=1.00 loops=1)
   Vectorized Filter: (ts = 500000)
   Rows Removed by Filter: 999
   ->  Index Scan using compress_hyper_6_26_chunk__ts_meta_min_1__ts_meta_max_1_idx on compress_hyper_6_26_chunk  (cost=0.28..18.70 rows=1 width=44) (actual rows=1.00 loops=1)
         Index Cond: ((_ts_meta_min_1 <= 500000) AND (_ts_meta_max_1 >= 500000))

explain (buffers off, analyze, timing off, summary off)
select * from highcard where ts < 500000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_5_25_chunk  (cost=0.32..5001.61 rows=498000 width=4) (actual rows=499999.00 loops=1)
   Vectorized Filter: (ts < 500000)
   Rows Removed by Filter: 1
   ->  Index Scan using compress_hyper_6_26_chunk__ts_meta_min_1__ts_meta_max_1_idx on compress_hyper_6_26_chunk  (cost=0.28..21.61 rows=498 width=44) (actual rows=500.00 loops=1)
         Index Cond: (_ts_meta_min_1 < 500000)

explain (buffers off, analyze, timing off, summary off)
select * from highcard where ts > 500000;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_5_25_chunk  (cost=0.05..5035.50 rows=501000 width=4) (actual rows=500000.00 loops=1)
   Vectorized Filter: (ts > 500000)
   ->  Seq Scan on compress_hyper_6_26_chunk  (cost=0.00..25.50 rows=501 width=44) (actual rows=500.00 loops=1)
         Filter: (_ts_meta_max_1 > 500000)
         Rows Removed by Filter: 500

-- Test for an OOB write we used to have where the max metadata column is the
-- last column in the compressed chunk table, as can happen on old versions.
create table lastmax(ts int) with (tsdb.hypertable, tsdb.partition_column = 'ts',
    tsdb.compress_orderby = 'ts');
insert into lastmax select generate_series(1, 1000);
select count(compress_chunk(x)) from show_chunks('lastmax') x;
 count 
-------
     1

vacuum freeze analyze lastmax;
select schema_name || '.' || table_name chunk, 'c' column from _timescaledb_catalog.chunk
    where id = (select compressed_chunk_id from _timescaledb_catalog.chunk
        where hypertable_id = (select id from _timescaledb_catalog.hypertable
            where table_name = 'lastmax') limit 1)
\gset
set timescaledb.restoring to true;
alter table :chunk drop column _ts_meta_max_1;
alter table :chunk add column _ts_meta_max_1 int default 1000;
reset timescaledb.restoring;
explain (costs off)
select * from lastmax where ts = 500;
--- QUERY PLAN ---
 Custom Scan (ColumnarScan) on _hyper_7_27_chunk
   Vectorized Filter: (ts = 500)
   ->  Seq Scan on compress_hyper_8_28_chunk
         Filter: ((_ts_meta_min_1 <= 500) AND (_ts_meta_max_1 >= 500))

