-- 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 path to check data intergrity after in-memory recompression
\set TEST_BASE_NAME recompression_intergrity_tests
-- requires TEST_BASE_NAME, TEST_TABLE_NAME, BATCH_METADATA_QUERY, ORDER_BY_CLAUSE
\set RECOMPRESSION_INTEGRITY_CHECK_RELPATH 'include/recompression_integrity_check.sql'
\set BATCH_METADATA_QUERY ''
\set ORDER_BY_CLAUSE ''
-- Test Case 1: Basic segmentby configuration
DROP TABLE IF EXISTS recomp_segmentby_test CASCADE;
NOTICE:  table "recomp_segmentby_test" does not exist, skipping
CREATE TABLE recomp_segmentby_test(
    time timestamptz NOT NULL,
    device text,
    location text,
    temperature float,
    humidity float
);
SELECT create_hypertable('recomp_segmentby_test','time') \gset
-- Set compression with single segmentby column
ALTER TABLE recomp_segmentby_test SET (
    timescaledb.compress,
    timescaledb.compress_segmentby='device',
    timescaledb.compress_orderby='time'
);
-- Insert test data across multiple time ranges
INSERT INTO recomp_segmentby_test VALUES
  ('2000-01-01 00:00:00', 'device1', 'room1', 20.5, 60.0),
  ('2000-01-01 01:00:00', 'device1', 'room1', 21.0, 61.5),
  ('2000-01-01 02:00:00', 'device2', 'room2', 19.8, 58.2),
  ('2000-01-02 00:00:00', 'device1', 'room1', 22.1, 63.0),
  ('2000-01-02 01:00:00', 'device2', 'room2', 18.9, 55.7),
  ('2001-01-01 00:00:00', 'device1', 'room1', 25.0, 70.0),
  ('2001-01-01 01:00:00', 'device2', 'room2', 23.5, 68.5);
\set TEST_TABLE_NAME 'recomp_segmentby_test'
SELECT compress_chunk(ch) FROM show_chunks(:'TEST_TABLE_NAME') ch;
             compress_chunk             
----------------------------------------
 _timescaledb_internal._hyper_1_1_chunk
 _timescaledb_internal._hyper_1_2_chunk

\ir :RECOMPRESSION_INTEGRITY_CHECK_RELPATH
-- 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.
SELECT format('%s/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESS",
       format('%s/results/%s_results_recompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_RECOMPRESS"
\gset
SELECT format('\! diff -u  --label "Compressed result" --label "Recompressed result" %s %s', :'TEST_RESULTS_COMPRESS', :'TEST_RESULTS_RECOMPRESS') as "DIFF_CMD"
\gset
-- Store initial compressed chunk info before recompression
SELECT uncompressed.schema_name || '.' || uncompressed.table_name AS "OLD_CHUNK_NAME",
        compressed.schema_name || '.' || compressed.table_name AS "OLD_COMPRESSED_CHUNK_NAME",
        compressed.id AS "OLD_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.hypertable_id = (
          SELECT id
          FROM _timescaledb_catalog.hypertable
          WHERE table_name = :'TEST_TABLE_NAME'
      )
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :OLD_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
\set QUERY1 'SELECT COUNT(*) FROM ' :OLD_CHUNK_NAME ';'
\set QUERY2 'SELECT * FROM ' :OLD_CHUNK_NAME :ORDER_BY_CLAUSE ';'
\o :TEST_RESULTS_COMPRESS
:QUERY1
:QUERY2
\o
-- Recompress the chunk in-memory
SELECT compress_chunk(:'OLD_CHUNK_NAME', recompress := true);
             compress_chunk             
----------------------------------------
 _timescaledb_internal._hyper_1_1_chunk

-- Get info for the new compressed chunk
SELECT compressed.schema_name || '.' || compressed.table_name AS "NEW_COMPRESSED_CHUNK_NAME",
        compressed.id AS "NEW_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.schema_name || '.' || uncompressed.table_name = :'OLD_CHUNK_NAME'
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :NEW_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
-- Get data after in-memory recompression
\o :TEST_RESULTS_RECOMPRESS
:QUERY1
:QUERY2
\o
-- Check if a new chunk was created (this will show in the output)
SELECT
  CASE WHEN :'NEW_CHUNK_ID' IS NULL OR :'OLD_CHUNK_ID' = :'NEW_CHUNK_ID' THEN
    'ERROR: Recompression did not create a new chunk'
  ELSE
    'SUCCESS: New chunk created, old_chunk_id=' || :'OLD_CHUNK_ID' || ', new_chunk_id=' || :'NEW_CHUNK_ID'
  END AS recompression_status;
                    recompression_status                    
------------------------------------------------------------
 SUCCESS: New chunk created, old_chunk_id=3, new_chunk_id=5

-- Compare result using diff to validate integrity of recompressed data
:DIFF_CMD
SELECT * FROM _timescaledb_catalog.compression_settings ORDER BY relid;
                 relid                  |                 compress_relid                 | segmentby | orderby | orderby_desc | orderby_nullsfirst |                            index                            
----------------------------------------+------------------------------------------------+-----------+---------+--------------+--------------------+-------------------------------------------------------------
 recomp_segmentby_test                  |                                                | {device}  | {time}  | {f}          | {f}                | 
 _timescaledb_internal._hyper_1_1_chunk | _timescaledb_internal.compress_hyper_2_5_chunk | {device}  | {time}  | {f}          | {f}                | [{"type": "minmax", "column": "time", "source": "orderby"}]
 _timescaledb_internal._hyper_1_2_chunk | _timescaledb_internal.compress_hyper_2_4_chunk | {device}  | {time}  | {f}          | {f}                | [{"type": "minmax", "column": "time", "source": "orderby"}]

DROP TABLE recomp_segmentby_test CASCADE;
-- Test Case 2: Multiple segmentby columns configuration
DROP TABLE IF EXISTS recomp_multi_segmentby_test CASCADE;
NOTICE:  table "recomp_multi_segmentby_test" does not exist, skipping
CREATE TABLE recomp_multi_segmentby_test(
    time timestamptz NOT NULL,
    device text,
    location text,
    sensor_type text,
    value float
);
SELECT create_hypertable('recomp_multi_segmentby_test','time') \gset
-- Set compression with multiple segmentby columns
ALTER TABLE recomp_multi_segmentby_test SET (
    timescaledb.compress,
    timescaledb.compress_segmentby='device,location',
    timescaledb.compress_orderby='time'
);
-- Insert test data
INSERT INTO recomp_multi_segmentby_test VALUES
  ('2000-01-01', 'device1', 'room1', 'temp', 20.5),
  ('2000-01-01', 'device1', 'room1', 'humidity', 60.0),
  ('2000-01-01', 'device1', 'room2', 'temp', 21.0),
  ('2000-01-01', 'device2', 'room1', 'temp', 19.8),
  ('2000-01-01', 'device2', 'room2', 'humidity', 58.2),
  ('2001-01-01', 'device1', 'room1', 'temp', 22.1),
  ('2001-01-01', 'device2', 'room1', 'humidity', 63.0);
\set TEST_TABLE_NAME 'recomp_multi_segmentby_test'
SELECT compress_chunk(ch) FROM show_chunks(:'TEST_TABLE_NAME') ch;
             compress_chunk             
----------------------------------------
 _timescaledb_internal._hyper_3_6_chunk
 _timescaledb_internal._hyper_3_7_chunk

\ir :RECOMPRESSION_INTEGRITY_CHECK_RELPATH
-- 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.
SELECT format('%s/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESS",
       format('%s/results/%s_results_recompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_RECOMPRESS"
\gset
SELECT format('\! diff -u  --label "Compressed result" --label "Recompressed result" %s %s', :'TEST_RESULTS_COMPRESS', :'TEST_RESULTS_RECOMPRESS') as "DIFF_CMD"
\gset
-- Store initial compressed chunk info before recompression
SELECT uncompressed.schema_name || '.' || uncompressed.table_name AS "OLD_CHUNK_NAME",
        compressed.schema_name || '.' || compressed.table_name AS "OLD_COMPRESSED_CHUNK_NAME",
        compressed.id AS "OLD_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.hypertable_id = (
          SELECT id
          FROM _timescaledb_catalog.hypertable
          WHERE table_name = :'TEST_TABLE_NAME'
      )
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :OLD_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
\set QUERY1 'SELECT COUNT(*) FROM ' :OLD_CHUNK_NAME ';'
\set QUERY2 'SELECT * FROM ' :OLD_CHUNK_NAME :ORDER_BY_CLAUSE ';'
\o :TEST_RESULTS_COMPRESS
:QUERY1
:QUERY2
\o
-- Recompress the chunk in-memory
SELECT compress_chunk(:'OLD_CHUNK_NAME', recompress := true);
             compress_chunk             
----------------------------------------
 _timescaledb_internal._hyper_3_6_chunk

-- Get info for the new compressed chunk
SELECT compressed.schema_name || '.' || compressed.table_name AS "NEW_COMPRESSED_CHUNK_NAME",
        compressed.id AS "NEW_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.schema_name || '.' || uncompressed.table_name = :'OLD_CHUNK_NAME'
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :NEW_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
-- Get data after in-memory recompression
\o :TEST_RESULTS_RECOMPRESS
:QUERY1
:QUERY2
\o
-- Check if a new chunk was created (this will show in the output)
SELECT
  CASE WHEN :'NEW_CHUNK_ID' IS NULL OR :'OLD_CHUNK_ID' = :'NEW_CHUNK_ID' THEN
    'ERROR: Recompression did not create a new chunk'
  ELSE
    'SUCCESS: New chunk created, old_chunk_id=' || :'OLD_CHUNK_ID' || ', new_chunk_id=' || :'NEW_CHUNK_ID'
  END AS recompression_status;
                    recompression_status                     
-------------------------------------------------------------
 SUCCESS: New chunk created, old_chunk_id=8, new_chunk_id=10

-- Compare result using diff to validate integrity of recompressed data
:DIFF_CMD
SELECT * FROM _timescaledb_catalog.compression_settings ORDER BY relid;
                 relid                  |                 compress_relid                  |     segmentby     | orderby | orderby_desc | orderby_nullsfirst |                            index                            
----------------------------------------+-------------------------------------------------+-------------------+---------+--------------+--------------------+-------------------------------------------------------------
 recomp_multi_segmentby_test            |                                                 | {device,location} | {time}  | {f}          | {f}                | 
 _timescaledb_internal._hyper_3_6_chunk | _timescaledb_internal.compress_hyper_4_10_chunk | {device,location} | {time}  | {f}          | {f}                | [{"type": "minmax", "column": "time", "source": "orderby"}]
 _timescaledb_internal._hyper_3_7_chunk | _timescaledb_internal.compress_hyper_4_9_chunk  | {device,location} | {time}  | {f}          | {f}                | [{"type": "minmax", "column": "time", "source": "orderby"}]

DROP TABLE recomp_multi_segmentby_test CASCADE;
-- Test Case 3: Sparse index configuration
DROP TABLE IF EXISTS recomp_index_test CASCADE;
NOTICE:  table "recomp_index_test" does not exist, skipping
CREATE TABLE recomp_index_test(
    x int,
    value text,
    u uuid,
    ts timestamp
) WITH (
    tsdb.hypertable,
    tsdb.partition_column='x',
    tsdb.segment_by='',
    tsdb.order_by='x',
    tsdb.index='bloom("u"),minmax("ts")'
);
-- Insert test data with sparse UUID pattern
INSERT INTO recomp_index_test
SELECT x, md5(x::text),
    CASE WHEN x = 7134 THEN '90ec9e8e-4501-4232-9d03-6d7cf6132815'::uuid
        ELSE '6c1d0998-05f3-452c-abd3-45afe72bbcab'::uuid END,
    '2021-01-01'::timestamp + (interval '1 hour') * x
FROM generate_series(1, 10000) x;
INSERT INTO recomp_index_test
SELECT x, md5(x::text),
    CASE WHEN x = 7134 THEN '90ec9e8e-4501-4232-9d03-6d7cf6132815'::uuid
        ELSE '6c1d0998-05f3-452c-abd3-45afe72bbcab'::uuid END,
    '2022-01-01'::timestamp + (interval '1 hour') * x
FROM generate_series(1, 10000) x;
\set TEST_TABLE_NAME 'recomp_index_test'
SELECT compress_chunk(ch) FROM show_chunks(:'TEST_TABLE_NAME') ch;
             compress_chunk              
-----------------------------------------
 _timescaledb_internal._hyper_5_11_chunk

\ir :RECOMPRESSION_INTEGRITY_CHECK_RELPATH
-- 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.
SELECT format('%s/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESS",
       format('%s/results/%s_results_recompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_RECOMPRESS"
\gset
SELECT format('\! diff -u  --label "Compressed result" --label "Recompressed result" %s %s', :'TEST_RESULTS_COMPRESS', :'TEST_RESULTS_RECOMPRESS') as "DIFF_CMD"
\gset
-- Store initial compressed chunk info before recompression
SELECT uncompressed.schema_name || '.' || uncompressed.table_name AS "OLD_CHUNK_NAME",
        compressed.schema_name || '.' || compressed.table_name AS "OLD_COMPRESSED_CHUNK_NAME",
        compressed.id AS "OLD_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.hypertable_id = (
          SELECT id
          FROM _timescaledb_catalog.hypertable
          WHERE table_name = :'TEST_TABLE_NAME'
      )
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :OLD_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
\set QUERY1 'SELECT COUNT(*) FROM ' :OLD_CHUNK_NAME ';'
\set QUERY2 'SELECT * FROM ' :OLD_CHUNK_NAME :ORDER_BY_CLAUSE ';'
\o :TEST_RESULTS_COMPRESS
:QUERY1
:QUERY2
\o
-- Recompress the chunk in-memory
SELECT compress_chunk(:'OLD_CHUNK_NAME', recompress := true);
             compress_chunk              
-----------------------------------------
 _timescaledb_internal._hyper_5_11_chunk

-- Get info for the new compressed chunk
SELECT compressed.schema_name || '.' || compressed.table_name AS "NEW_COMPRESSED_CHUNK_NAME",
        compressed.id AS "NEW_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.schema_name || '.' || uncompressed.table_name = :'OLD_CHUNK_NAME'
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :NEW_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
-- Get data after in-memory recompression
\o :TEST_RESULTS_RECOMPRESS
:QUERY1
:QUERY2
\o
-- Check if a new chunk was created (this will show in the output)
SELECT
  CASE WHEN :'NEW_CHUNK_ID' IS NULL OR :'OLD_CHUNK_ID' = :'NEW_CHUNK_ID' THEN
    'ERROR: Recompression did not create a new chunk'
  ELSE
    'SUCCESS: New chunk created, old_chunk_id=' || :'OLD_CHUNK_ID' || ', new_chunk_id=' || :'NEW_CHUNK_ID'
  END AS recompression_status;
                     recompression_status                     
--------------------------------------------------------------
 SUCCESS: New chunk created, old_chunk_id=12, new_chunk_id=13

-- Compare result using diff to validate integrity of recompressed data
:DIFF_CMD
SELECT * FROM _timescaledb_catalog.compression_settings ORDER BY relid;
                  relid                  |                 compress_relid                  | segmentby | orderby | orderby_desc | orderby_nullsfirst |                                                                                 index                                                                                  
-----------------------------------------+-------------------------------------------------+-----------+---------+--------------+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 recomp_index_test                       |                                                 | {}        | {x}     | {f}          | {f}                | [{"type": "bloom", "column": "u", "source": "config"}, {"type": "minmax", "column": "ts", "source": "config"}, {"type": "minmax", "column": "x", "source": "orderby"}]
 _timescaledb_internal._hyper_5_11_chunk | _timescaledb_internal.compress_hyper_6_13_chunk | {}        | {x}     | {f}          | {f}                | [{"type": "bloom", "column": "u", "source": "config"}, {"type": "minmax", "column": "ts", "source": "config"}, {"type": "minmax", "column": "x", "source": "orderby"}]

DROP TABLE recomp_index_test CASCADE;
-- Test Case 4: Large dataset
DROP TABLE IF EXISTS recomp_large_data_test CASCADE;
NOTICE:  table "recomp_large_data_test" does not exist, skipping
CREATE TABLE recomp_large_data_test(
    x int,
    value text,
    u uuid,
    ts timestamp
) WITH (
    tsdb.hypertable,
    tsdb.partition_column='ts',
    tsdb.segment_by='',
    tsdb.order_by='ts',
    tsdb.index=''
);
-- Insert test data with sparse UUID pattern
INSERT INTO recomp_large_data_test
SELECT x, md5(x::text),
    CASE WHEN x = 7134 THEN '90ec9e8e-4501-4232-9d03-6d7cf6132815'::uuid
        ELSE '6c1d0998-05f3-452c-abd3-45afe72bbcab'::uuid END,
    '2021-01-01'::timestamp + (interval '1 second') * x
FROM generate_series(1, 10000) x;
INSERT INTO recomp_large_data_test
SELECT x, md5(x::text),
    CASE WHEN x = 7134 THEN '90ec9e8e-4501-4232-9d03-6d7cf6132815'::uuid
        ELSE '6c1d0998-05f3-452c-abd3-45afe72bbcab'::uuid END,
    '2022-01-01'::timestamp + (interval '1 second') * x
FROM generate_series(1, 10000) x;
\set TEST_TABLE_NAME 'recomp_large_data_test'
-- Compress all uncompressed chunks
SELECT compress_chunk(ch) FROM show_chunks(:'TEST_TABLE_NAME') ch;
             compress_chunk              
-----------------------------------------
 _timescaledb_internal._hyper_7_14_chunk
 _timescaledb_internal._hyper_7_15_chunk

\ir :RECOMPRESSION_INTEGRITY_CHECK_RELPATH
-- 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.
SELECT format('%s/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESS",
       format('%s/results/%s_results_recompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_RECOMPRESS"
\gset
SELECT format('\! diff -u  --label "Compressed result" --label "Recompressed result" %s %s', :'TEST_RESULTS_COMPRESS', :'TEST_RESULTS_RECOMPRESS') as "DIFF_CMD"
\gset
-- Store initial compressed chunk info before recompression
SELECT uncompressed.schema_name || '.' || uncompressed.table_name AS "OLD_CHUNK_NAME",
        compressed.schema_name || '.' || compressed.table_name AS "OLD_COMPRESSED_CHUNK_NAME",
        compressed.id AS "OLD_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.hypertable_id = (
          SELECT id
          FROM _timescaledb_catalog.hypertable
          WHERE table_name = :'TEST_TABLE_NAME'
      )
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :OLD_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
\set QUERY1 'SELECT COUNT(*) FROM ' :OLD_CHUNK_NAME ';'
\set QUERY2 'SELECT * FROM ' :OLD_CHUNK_NAME :ORDER_BY_CLAUSE ';'
\o :TEST_RESULTS_COMPRESS
:QUERY1
:QUERY2
\o
-- Recompress the chunk in-memory
SELECT compress_chunk(:'OLD_CHUNK_NAME', recompress := true);
             compress_chunk              
-----------------------------------------
 _timescaledb_internal._hyper_7_14_chunk

-- Get info for the new compressed chunk
SELECT compressed.schema_name || '.' || compressed.table_name AS "NEW_COMPRESSED_CHUNK_NAME",
        compressed.id AS "NEW_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.schema_name || '.' || uncompressed.table_name = :'OLD_CHUNK_NAME'
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :NEW_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
-- Get data after in-memory recompression
\o :TEST_RESULTS_RECOMPRESS
:QUERY1
:QUERY2
\o
-- Check if a new chunk was created (this will show in the output)
SELECT
  CASE WHEN :'NEW_CHUNK_ID' IS NULL OR :'OLD_CHUNK_ID' = :'NEW_CHUNK_ID' THEN
    'ERROR: Recompression did not create a new chunk'
  ELSE
    'SUCCESS: New chunk created, old_chunk_id=' || :'OLD_CHUNK_ID' || ', new_chunk_id=' || :'NEW_CHUNK_ID'
  END AS recompression_status;
                     recompression_status                     
--------------------------------------------------------------
 SUCCESS: New chunk created, old_chunk_id=16, new_chunk_id=18

-- Compare result using diff to validate integrity of recompressed data
:DIFF_CMD
SELECT * FROM _timescaledb_catalog.compression_settings ORDER BY relid;
                  relid                  |                 compress_relid                  | segmentby | orderby | orderby_desc | orderby_nullsfirst |                                      index                                      
-----------------------------------------+-------------------------------------------------+-----------+---------+--------------+--------------------+---------------------------------------------------------------------------------
 recomp_large_data_test                  |                                                 | {}        | {ts}    | {f}          | {f}                | [{"source": "config"}, {"type": "minmax", "column": "ts", "source": "orderby"}]
 _timescaledb_internal._hyper_7_14_chunk | _timescaledb_internal.compress_hyper_8_18_chunk | {}        | {ts}    | {f}          | {f}                | [{"source": "config"}, {"type": "minmax", "column": "ts", "source": "orderby"}]
 _timescaledb_internal._hyper_7_15_chunk | _timescaledb_internal.compress_hyper_8_17_chunk | {}        | {ts}    | {f}          | {f}                | [{"source": "config"}, {"type": "minmax", "column": "ts", "source": "orderby"}]

DROP TABLE recomp_large_data_test CASCADE;
-- Setup Direct Compress
-- Batches may be uneven with direct compress but should always be even after recompression.
\set BATCH_METADATA_QUERY 'SELECT _ts_meta_count, _ts_meta_min_1, _ts_meta_max_1 FROM :COMPRESSED_CHUNK_NAME;'
SET timescaledb.enable_direct_compress_insert = true;
SET timescaledb.enable_direct_compress_insert_sort_batches = true;
SET timescaledb.enable_direct_compress_insert_client_sorted = true;
-- Test Case 5: Direct Compress Batches
DROP TABLE IF EXISTS recomp_direct_compress CASCADE;
NOTICE:  table "recomp_direct_compress" does not exist, skipping
CREATE TABLE recomp_direct_compress (time TIMESTAMPTZ NOT NULL, device TEXT, value float) WITH (tsdb.hypertable, tsdb.orderby='time');
NOTICE:  using column "time" as partitioning column
-- Insert uneven batches
SELECT setseed(0.5);
 setseed 
---------
 

DO $$
DECLARE
start_pos int := 0;
batch_size int;
i int;
BEGIN
FOR i IN 1..5 LOOP  -- 5 random batches
    batch_size := (random() * 200 + 50)::int;  -- Random size 50-250
    EXECUTE format('INSERT INTO recomp_direct_compress SELECT ''2025-01-01''::timestamptz + (i || '' minute'')::interval, ''d1'', i::float FROM generate_series(%s,%s) i',
                    start_pos, start_pos + batch_size - 1);

    start_pos := start_pos + batch_size;
END LOOP;
END $$;
-- Will use in-memory recompression since client_sorted = true
\set TEST_TABLE_NAME 'recomp_direct_compress'
\ir :RECOMPRESSION_INTEGRITY_CHECK_RELPATH
-- 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.
SELECT format('%s/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESS",
       format('%s/results/%s_results_recompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_RECOMPRESS"
\gset
SELECT format('\! diff -u  --label "Compressed result" --label "Recompressed result" %s %s', :'TEST_RESULTS_COMPRESS', :'TEST_RESULTS_RECOMPRESS') as "DIFF_CMD"
\gset
-- Store initial compressed chunk info before recompression
SELECT uncompressed.schema_name || '.' || uncompressed.table_name AS "OLD_CHUNK_NAME",
        compressed.schema_name || '.' || compressed.table_name AS "OLD_COMPRESSED_CHUNK_NAME",
        compressed.id AS "OLD_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.hypertable_id = (
          SELECT id
          FROM _timescaledb_catalog.hypertable
          WHERE table_name = :'TEST_TABLE_NAME'
      )
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :OLD_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
 _ts_meta_count |        _ts_meta_min_1        |        _ts_meta_max_1        
----------------+------------------------------+------------------------------
            247 | Wed Jan 01 00:00:00 2025 PST | Wed Jan 01 04:06:00 2025 PST
            215 | Wed Jan 01 04:07:00 2025 PST | Wed Jan 01 07:41:00 2025 PST
             76 | Wed Jan 01 07:42:00 2025 PST | Wed Jan 01 08:57:00 2025 PST
             83 | Wed Jan 01 08:58:00 2025 PST | Wed Jan 01 10:20:00 2025 PST
            180 | Wed Jan 01 10:21:00 2025 PST | Wed Jan 01 13:20:00 2025 PST

\set QUERY1 'SELECT COUNT(*) FROM ' :OLD_CHUNK_NAME ';'
\set QUERY2 'SELECT * FROM ' :OLD_CHUNK_NAME :ORDER_BY_CLAUSE ';'
\o :TEST_RESULTS_COMPRESS
:QUERY1
:QUERY2
\o
-- Recompress the chunk in-memory
SELECT compress_chunk(:'OLD_CHUNK_NAME', recompress := true);
             compress_chunk              
-----------------------------------------
 _timescaledb_internal._hyper_9_19_chunk

-- Get info for the new compressed chunk
SELECT compressed.schema_name || '.' || compressed.table_name AS "NEW_COMPRESSED_CHUNK_NAME",
        compressed.id AS "NEW_CHUNK_ID"
FROM _timescaledb_catalog.chunk uncompressed
JOIN _timescaledb_catalog.chunk compressed
  ON uncompressed.compressed_chunk_id = compressed.id
WHERE uncompressed.schema_name || '.' || uncompressed.table_name = :'OLD_CHUNK_NAME'
LIMIT 1 \gset
\set COMPRESSED_CHUNK_NAME :NEW_COMPRESSED_CHUNK_NAME
:BATCH_METADATA_QUERY
 _ts_meta_count |        _ts_meta_min_1        |        _ts_meta_max_1        
----------------+------------------------------+------------------------------
            801 | Wed Jan 01 00:00:00 2025 PST | Wed Jan 01 13:20:00 2025 PST

-- Get data after in-memory recompression
\o :TEST_RESULTS_RECOMPRESS
:QUERY1
:QUERY2
\o
-- Check if a new chunk was created (this will show in the output)
SELECT
  CASE WHEN :'NEW_CHUNK_ID' IS NULL OR :'OLD_CHUNK_ID' = :'NEW_CHUNK_ID' THEN
    'ERROR: Recompression did not create a new chunk'
  ELSE
    'SUCCESS: New chunk created, old_chunk_id=' || :'OLD_CHUNK_ID' || ', new_chunk_id=' || :'NEW_CHUNK_ID'
  END AS recompression_status;
                     recompression_status                     
--------------------------------------------------------------
 SUCCESS: New chunk created, old_chunk_id=20, new_chunk_id=21

-- Compare result using diff to validate integrity of recompressed data
:DIFF_CMD
SELECT * FROM _timescaledb_catalog.compression_settings ORDER BY relid;
                  relid                  |                  compress_relid                  | segmentby | orderby | orderby_desc | orderby_nullsfirst |                            index                            
-----------------------------------------+--------------------------------------------------+-----------+---------+--------------+--------------------+-------------------------------------------------------------
 recomp_direct_compress                  |                                                  |           | {time}  | {f}          | {f}                | 
 _timescaledb_internal._hyper_9_19_chunk | _timescaledb_internal.compress_hyper_10_21_chunk |           | {time}  | {f}          | {f}                | [{"type": "minmax", "column": "time", "source": "orderby"}]

DROP TABLE IF EXISTS recomp_direct_compress CASCADE;
RESET timescaledb.enable_direct_compress_insert;
RESET timescaledb.enable_direct_compress_insert_sort_batches;
RESET timescaledb.enable_direct_compress_insert_client_sorted;
-- Test Case 6: Disabled in-memory recompression GUC
CREATE TABLE recomp_guc_test(
    time timestamptz NOT NULL,
    device text,
    value float
);
SELECT create_hypertable('recomp_guc_test','time') \gset
ALTER TABLE recomp_guc_test SET (
    timescaledb.compress,
    timescaledb.compress_segmentby='device',
    timescaledb.compress_orderby='time'
);
SET timescaledb.enable_in_memory_recompression = off;
-- Insert test data
INSERT INTO recomp_guc_test VALUES ('2000-01-01 00:00:00', 'device1', 20.5);
SELECT ch AS chunk FROM show_chunks('recomp_guc_test') ch ORDER BY ch LIMIT 1 \gset
CALL convert_to_columnstore(:'chunk');
CALL convert_to_columnstore(:'chunk', recompress := true);
-- Verify data integrity after fallback recompression
SELECT COUNT(*) FROM recomp_guc_test;
 count 
-------
     1

SELECT * FROM recomp_guc_test ORDER BY time, device;
             time             | device  | value 
------------------------------+---------+-------
 Sat Jan 01 00:00:00 2000 PST | device1 |  20.5

RESET timescaledb.enable_in_memory_recompression;
DROP TABLE recomp_guc_test CASCADE;
