-- 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 PREFIX 'EXPLAIN (analyze, buffers off, costs off, timing off, summary off)'
\set PREFIX_VERBOSE 'EXPLAIN (analyze, buffers off, costs off, timing off, summary off, verbose)'
\set PREFIX_NO_ANALYZE 'EXPLAIN (verbose, buffers off, costs off)'
\set PREFIX_NO_VERBOSE 'EXPLAIN (buffers off, costs off)'

-- Some tweaks to get less flaky test
SET parallel_leader_participation TO off;
SET min_parallel_table_scan_size TO '0';
SET enable_incremental_sort TO off;

SELECT show_chunks('metrics_compressed') AS "TEST_TABLE" ORDER BY 1::text LIMIT 1 \gset

-- this should use ColumnarScan node
:PREFIX_VERBOSE
SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY time LIMIT 5;

-- must not use ColumnarScan node
:PREFIX_VERBOSE
SELECT * FROM ONLY :TEST_TABLE WHERE device_id = 1 ORDER BY time LIMIT 5;

-- this should use ColumnarScan node
:PREFIX_NO_VERBOSE
SELECT * FROM :TEST_TABLE UNION ALL SELECT * FROM :TEST_TABLE;

:PREFIX_NO_VERBOSE
SELECT * FROM :TEST_TABLE UNION ALL SELECT * FROM :TEST_TABLE ORDER BY time;

-- test expressions
:PREFIX
SELECT time_bucket ('1d', time),
    v1 + v2 AS "sum",
    COALESCE(NULL, v1, v2) AS "coalesce",
    NULL AS "NULL",
    'text' AS "text",
    t AS "RECORD"
FROM :TEST_TABLE t
WHERE device_id IN (1, 2)
ORDER BY time, device_id;

-- test empty targetlist
:PREFIX SELECT FROM :TEST_TABLE;

-- test empty resultset
:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id < 0;

-- test targetlist not referencing columns
:PREFIX SELECT 1 FROM :TEST_TABLE;

-- test constraints not present in targetlist
:PREFIX SELECT v1 FROM :TEST_TABLE WHERE device_id = 1 ORDER BY v1;

-- test order not present in targetlist
:PREFIX SELECT v2 FROM :TEST_TABLE WHERE device_id = 1 ORDER BY v1;

-- test column with all NULL
:PREFIX SELECT v3 FROM :TEST_TABLE WHERE device_id = 1;

--
-- test qual pushdown
--
-- v3 is not segment by or order by column so should not be pushed down
:PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE v3 > 10.0 ORDER BY time, device_id;

-- device_id constraint should be pushed down
:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY time, device_id LIMIT 10;

-- test IS NULL / IS NOT NULL
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id IS NOT NULL ORDER BY time, device_id LIMIT 10;

:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id IS NULL ORDER BY time, device_id LIMIT 10;

-- test IN (Const,Const)
:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id IN (1, 2) ORDER BY time, device_id LIMIT 10;

-- test cast pushdown
:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id = '1'::text::int ORDER BY time, device_id LIMIT 10;

--test var op var
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = v0 ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id < v1 ORDER BY time, device_id LIMIT 10;

-- test expressions
:PREFIX SELECT * FROM :TEST_TABLE WHERE device_id = 1 + 4 / 2 ORDER BY time, device_id LIMIT 10;

-- test function calls
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = length(substring(version(), 1, 3)) ORDER BY time, device_id LIMIT 10;

--
-- test segment meta pushdown
--
-- order by column and const
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time = '2000-01-01 1:00:00+0' ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time < '2000-01-01 1:00:00+0' ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time <= '2000-01-01 1:00:00+0' ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time >= '2000-01-01 1:00:00+0' ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time > '2000-01-01 1:00:00+0' ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE '2000-01-01 1:00:00+0' < time ORDER BY time, device_id LIMIT 10;

--pushdowns between order by and segment by columns
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE v0 < 1 ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE v0 < device_id ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id < v0 ORDER BY time, device_id LIMIT 10;
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE v1 = device_id ORDER BY time, device_id LIMIT 10;

--pushdown between two order by column (not pushed down)
:PREFIX_NO_ANALYZE SELECT * FROM :TEST_TABLE WHERE v0 = v1 ORDER BY time, device_id LIMIT 10;

--pushdown of quals on order by and segment by cols anded together
:PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE time > '2000-01-01 1:00:00+0' AND device_id = 1 ORDER BY time, device_id LIMIT 10;

--pushdown of quals on order by and segment by cols or together (not pushed down)
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time > '2000-01-01 1:00:00+0' OR device_id = 1 ORDER BY time, device_id LIMIT 10;

--functions optimized as well
:PREFIX_NO_VERBOSE SELECT * FROM :TEST_TABLE WHERE time < now() ORDER BY time, device_id LIMIT 10;

-- test sort optimization interaction
:PREFIX_NO_VERBOSE SELECT time FROM :TEST_TABLE ORDER BY time DESC LIMIT 10;

:PREFIX_NO_ANALYZE SELECT time, device_id FROM :TEST_TABLE ORDER BY time DESC, device_id LIMIT 10;

:PREFIX_NO_VERBOSE SELECT time, device_id FROM :TEST_TABLE ORDER BY device_id, time DESC LIMIT 10;

-- test aggregate
:PREFIX_NO_ANALYZE SELECT count(*) FROM :TEST_TABLE;

-- test aggregate with GROUP BY
:PREFIX_NO_ANALYZE SELECT count(*) FROM :TEST_TABLE GROUP BY device_id ORDER BY device_id;

-- test window functions with GROUP BY
:PREFIX_NO_ANALYZE SELECT sum(count(*)) OVER () FROM :TEST_TABLE GROUP BY device_id ORDER BY device_id;

-- test CTE
:PREFIX WITH q AS (
  SELECT v1 FROM :TEST_TABLE ORDER BY time
)
SELECT * FROM q ORDER BY v1;

-- test CTE join
:PREFIX WITH q1 AS (
  SELECT time, v1 FROM :TEST_TABLE WHERE device_id = 1 ORDER BY time
),
q2 AS (
  SELECT time, v2 FROM :TEST_TABLE WHERE device_id = 2 ORDER BY time
)
SELECT * FROM q1 INNER JOIN q2 ON q1.time = q2.time ORDER BY q1.time;

--
-- test indexes
--
SET enable_seqscan TO FALSE;

-- IndexScans should work
:PREFIX_VERBOSE SELECT time, device_id FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time;

-- globs should not plan IndexOnlyScans
:PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time;

-- whole row reference should work
:PREFIX_VERBOSE SELECT test_table FROM :TEST_TABLE AS test_table WHERE device_id = 1 ORDER BY device_id, time;

-- even when we select only a segmentby column, we still need count
:PREFIX_VERBOSE SELECT device_id FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id;

:PREFIX_VERBOSE SELECT count(*) FROM :TEST_TABLE WHERE device_id = 1;

--ensure that we can get a nested loop
SET enable_seqscan TO TRUE;
SET enable_hashjoin TO FALSE;

:PREFIX_VERBOSE SELECT device_id FROM :TEST_TABLE WHERE device_id IN ( VALUES (1));

--with multiple values can get a nested loop.
:PREFIX_VERBOSE SELECT device_id FROM :TEST_TABLE WHERE device_id IN ( VALUES (1), (2));

RESET enable_hashjoin;

:PREFIX_VERBOSE SELECT device_id FROM :TEST_TABLE WHERE device_id IN (VALUES (1));

--with multiple values can get a semi-join or nested loop depending on seq_page_cost.
:PREFIX_VERBOSE SELECT device_id FROM :TEST_TABLE WHERE device_id IN (VALUES (1), (2));

SET seq_page_cost = 100;

-- loop/row counts of this query is different on windows so we run it without analyze
:PREFIX_NO_ANALYZE SELECT device_id FROM :TEST_TABLE WHERE device_id IN (VALUES (1), (2));

RESET seq_page_cost;

-- test view
CREATE OR REPLACE VIEW compressed_view AS SELECT time, device_id, v1, v2 FROM :TEST_TABLE;

:PREFIX SELECT * FROM compressed_view WHERE device_id = 1 ORDER BY time DESC LIMIT 10;

DROP VIEW compressed_view;

-- test INNER JOIN
:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1
    INNER JOIN :TEST_TABLE m2 ON m1.time = m2.time
        AND m1.device_id = m2.device_id
    ORDER BY m1.time,
        m1.device_id
    LIMIT 10;

:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1
    INNER JOIN :TEST_TABLE m2 ON m1.time = m2.time
    INNER JOIN :TEST_TABLE m3 ON m2.time = m3.time
        AND m1.device_id = m2.device_id
        AND m3.device_id = 3
    ORDER BY m1.time,
        m1.device_id
    LIMIT 10;
RESET min_parallel_table_scan_size;

:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1
    INNER JOIN :TEST_TABLE m2 ON m1.time = m2.time
        AND m1.device_id = 1
        AND m2.device_id = 2
    ORDER BY m1.time,
        m1.device_id,
        m2.time,
        m2.device_id
    LIMIT 100;

:PREFIX_NO_VERBOSE
SELECT *
FROM metrics m1
    INNER JOIN metrics_space m2 ON m1.time = m2.time
        AND m1.device_id = 1
        AND m2.device_id = 2
	WHERE m1.v1 = 3 -- additional filter to force m1 as inner relation in join
    ORDER BY m1.time,
        m1.device_id,
        m2.time,
        m2.device_id
    LIMIT 100;

-- test OUTER JOIN
SET min_parallel_table_scan_size TO '0';
-- Disable merge join to get stable tests. These queries uses hash
-- joins.
SET enable_mergejoin TO off;
:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1
    LEFT OUTER JOIN :TEST_TABLE m2 ON m1.time = m2.time
    AND m1.device_id = m2.device_id
ORDER BY m1.time,
    m1.device_id
LIMIT 10;
RESET min_parallel_table_scan_size;

:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1
    LEFT OUTER JOIN :TEST_TABLE m2 ON m1.time = m2.time
    AND m1.device_id = 1
    AND m2.device_id = 2
ORDER BY m1.time,
    m1.device_id,
    m2.time,
    m2.device_id
LIMIT 100;

-- test implicit self-join
:PREFIX_NO_VERBOSE
SELECT *
FROM :TEST_TABLE m1,
    :TEST_TABLE m2
WHERE m1.time = m2.time
ORDER BY m1.time,
    m1.device_id,
    m2.time,
    m2.device_id
LIMIT 20;

-- test self-join with sub-query
:PREFIX_NO_VERBOSE
SELECT *
FROM (
    SELECT *
    FROM :TEST_TABLE m1) m1
    INNER JOIN (
        SELECT *
        FROM :TEST_TABLE m2) m2 ON m1.time = m2.time
ORDER BY m1.time,
    m1.device_id,
    m2.device_id
LIMIT 10;
RESET enable_mergejoin;

:PREFIX
SELECT *
FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d'::interval) g (time)
        INNER JOIN LATERAL (
            SELECT time
            FROM :TEST_TABLE m1
            WHERE m1.time = g.time
            LIMIT 1) m1 ON TRUE;


-- test prepared statement
SET plan_cache_mode TO force_generic_plan;
PREPARE prep AS SELECT count(time) FROM :TEST_TABLE WHERE device_id = 1;

:PREFIX EXECUTE prep;
EXECUTE prep;
EXECUTE prep;
EXECUTE prep;
EXECUTE prep;
EXECUTE prep;
EXECUTE prep;
DEALLOCATE prep;

-- test prepared statement with params pushdown
PREPARE param_prep (int) AS
SELECT *
FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d'::interval) g (time)
        INNER JOIN LATERAL (
            SELECT time
            FROM :TEST_TABLE m1
            WHERE m1.time = g.time
                AND device_id = $1
            LIMIT 1) m1 ON TRUE;

:PREFIX EXECUTE param_prep (1);
:PREFIX EXECUTE param_prep (2);
EXECUTE param_prep (1);
EXECUTE param_prep (2);
EXECUTE param_prep (1);
EXECUTE param_prep (2);
EXECUTE param_prep (1);
DEALLOCATE param_prep;
RESET plan_cache_mode;

-- test hypertable being non-toplevel equivalence member #6925
CREATE TABLE i6925_t1(observed timestamptz not null, queryid int8, total_exec_time int8);
CREATE TABLE i6925_t2(LIKE i6925_t1);

SELECT table_name FROM create_hypertable('i6925_t1', 'observed');
ALTER TABLE i6925_t1 SET (timescaledb.compress, timescaledb.compress_segmentby = 'queryid');
INSERT INTO i6925_t1 SELECT '2020-01-01', 1, 1;
SELECT count(compress_chunk(chunk_name)) FROM show_chunks('i6925_t1') chunk_name;

SELECT queryid, lag(total_exec_time) OVER (PARTITION BY queryid) FROM (SELECT * FROM i6925_t1 UNION ALL SELECT * FROM i6925_t2) q;

DROP TABLE i6925_t1;
DROP TABLE i6925_t2;
