-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.

\set PREFIX 'EXPLAIN (VERBOSE, BUFFERS OFF, COSTS OFF)'

-- Create a two dimensional hypertable
CREATE TABLE hyper (time timestamptz, device int, temp float);
SELECT * FROM create_hypertable('hyper', 'time', 'device', 2);

-- Create a similar PostgreSQL partitioned table
CREATE TABLE pg2dim (time timestamptz, device int, temp float) PARTITION BY HASH (device);
CREATE TABLE pg2dim_h1 PARTITION OF pg2dim FOR VALUES WITH (MODULUS 2, REMAINDER 0) PARTITION BY RANGE(time);
CREATE TABLE pg2dim_h2 PARTITION OF pg2dim FOR VALUES WITH (MODULUS 2, REMAINDER 1) PARTITION BY RANGE(time);
CREATE TABLE pg2dim_h1_t1 PARTITION OF pg2dim_h1 FOR VALUES FROM ('2018-01-01 00:00') TO ('2018-09-01 00:00');
CREATE TABLE pg2dim_h1_t2 PARTITION OF pg2dim_h1 FOR VALUES FROM ('2018-09-01 00:00') TO ('2018-12-01 00:00');
CREATE TABLE pg2dim_h2_t1 PARTITION OF pg2dim_h2 FOR VALUES FROM ('2018-01-01 00:00') TO ('2018-09-01 00:00');
CREATE TABLE pg2dim_h2_t2 PARTITION OF pg2dim_h2 FOR VALUES FROM ('2018-09-01 00:00') TO ('2018-12-01 00:00');

-- Create a 1-dimensional partitioned table for comparison
CREATE TABLE pg1dim (time timestamptz, device int, temp float) PARTITION BY HASH (device);
CREATE TABLE pg1dim_h1 PARTITION OF pg1dim FOR VALUES WITH (MODULUS 2, REMAINDER 0);
CREATE TABLE pg1dim_h2 PARTITION OF pg1dim FOR VALUES WITH (MODULUS 2, REMAINDER 1);

INSERT INTO hyper VALUES
       ('2018-02-19 13:01', 1, 2.3),
       ('2018-02-19 13:02', 3, 3.1),
       ('2018-10-19 13:01', 1, 7.6),
       ('2018-10-19 13:02', 3, 9.0);

INSERT INTO pg2dim VALUES
       ('2018-02-19 13:01', 1, 2.3),
       ('2018-02-19 13:02', 3, 3.1),
       ('2018-10-19 13:01', 1, 7.6),
       ('2018-10-19 13:02', 3, 9.0);

INSERT INTO pg1dim VALUES
       ('2018-02-19 13:01', 1, 2.3),
       ('2018-02-19 13:02', 3, 3.1),
       ('2018-10-19 13:01', 1, 7.6),
       ('2018-10-19 13:02', 3, 9.0);

SELECT * FROM test.show_subtables('hyper');

SELECT * FROM pg2dim_h1_t1;
SELECT * FROM pg2dim_h1_t2;
SELECT * FROM pg2dim_h2_t1;
SELECT * FROM pg2dim_h2_t2;


-- Compare partitionwise aggreate enabled/disabled. First run queries
-- on PG partitioned tables for reference.

-- All partition keys covered by GROUP BY
SET enable_partitionwise_aggregate = 'off';
:PREFIX
SELECT device, avg(temp)
FROM pg1dim
GROUP BY 1
ORDER BY 1;

SET enable_partitionwise_aggregate = 'on';
:PREFIX
SELECT device, avg(temp)
FROM pg1dim
GROUP BY 1
ORDER BY 1;

-- All partition keys not covered by GROUP BY (partial partitionwise)
SET enable_partitionwise_aggregate = 'off';
:PREFIX
SELECT device, avg(temp)
FROM pg2dim
GROUP BY 1
ORDER BY 1;

SET enable_partitionwise_aggregate = 'on';
:PREFIX
SELECT device, avg(temp)
FROM pg2dim
GROUP BY 1
ORDER BY 1;

-- All partition keys covered by GROUP BY (full partitionwise)
SET enable_partitionwise_aggregate = 'off';
:PREFIX
SELECT time, device, avg(temp)
FROM pg2dim
GROUP BY 1, 2
ORDER BY 1, 2;

SET enable_partitionwise_aggregate = 'on';
:PREFIX
SELECT time, device, avg(temp)
FROM pg2dim
GROUP BY 1, 2
ORDER BY 1, 2;

-- All partition keys not covered by GROUP BY because of date_trunc
-- expression on time (partial partitionwise)
SET enable_partitionwise_aggregate = 'off';
:PREFIX
SELECT date_trunc('month', time), device, avg(temp)
FROM pg2dim
GROUP BY 1, 2
ORDER BY 1, 2;

SET enable_partitionwise_aggregate = 'on';
:PREFIX
SELECT date_trunc('month', time), device, avg(temp)
FROM pg2dim
GROUP BY 1, 2
ORDER BY 1, 2;

-- Now run on hypertable

-- All partition keys not covered by GROUP BY (partial partitionwise)
SET timescaledb.enable_chunkwise_aggregation = 'off';
:PREFIX
SELECT device, avg(temp)
FROM hyper
GROUP BY 1
ORDER BY 1;

SET timescaledb.enable_chunkwise_aggregation = 'on';
:PREFIX
SELECT device, avg(temp)
FROM hyper
GROUP BY 1
ORDER BY 1;

-- All partition keys covered (full partitionwise)
SET timescaledb.enable_chunkwise_aggregation = 'off';
:PREFIX
SELECT time, device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

SET timescaledb.enable_chunkwise_aggregation = 'on';
:PREFIX
SELECT time, device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

-- Partial aggregation since date_trunc(time) is not a partition key
SET enable_partitionwise_aggregate = 'off';
:PREFIX
SELECT date_trunc('month', time), device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

-- Partial aggregation pushdown is currently not supported for this query by
-- the TSDB pushdown code since a projection is used in the path.
SET enable_partitionwise_aggregate = 'on';
:PREFIX
SELECT date_trunc('month', time), device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

-- Also test time_bucket
SET timescaledb.enable_chunkwise_aggregation = 'off';
:PREFIX
SELECT time_bucket('1 month', time), device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

SET timescaledb.enable_chunkwise_aggregation = 'on';
:PREFIX
SELECT time_bucket('1 month', time), device, avg(temp)
FROM hyper
GROUP BY 1, 2
ORDER BY 1, 2;

-- Test partitionwise joins, mostly to see that we do not break
-- anything
CREATE TABLE hyper_meta (time timestamptz, device int, info text);
SELECT * FROM create_hypertable('hyper_meta', 'time', 'device', 2);

INSERT INTO hyper_meta VALUES
       ('2018-02-19 13:01', 1, 'device_1'),
       ('2018-02-19 13:02', 3, 'device_3');

SET enable_partitionwise_join = 'off';

:PREFIX
SELECT h.time, h.device, h.temp, hm.info
FROM hyper h, hyper_meta hm
WHERE h.device = hm.device;

:PREFIX
SELECT pg2.time, pg2.device, pg2.temp, pg1.temp
FROM pg2dim pg2, pg1dim pg1
WHERE pg2.device = pg1.device;

SET enable_partitionwise_join = 'on';

:PREFIX
SELECT h.time, h.device, h.temp, hm.info
FROM hyper h, hyper_meta hm
WHERE h.device = hm.device;

:PREFIX
SELECT pg2.time, pg2.device, pg2.temp, pg1.temp
FROM pg2dim pg2, pg1dim pg1
WHERE pg2.device = pg1.device;

-- Test hypertable with time partitioning function
CREATE OR REPLACE FUNCTION time_func(unixtime float8)
    RETURNS TIMESTAMPTZ LANGUAGE PLPGSQL IMMUTABLE AS
$BODY$
DECLARE
    retval TIMESTAMPTZ;
BEGIN
    retval := to_timestamp(unixtime);
    RETURN retval;
END
$BODY$;

CREATE TABLE hyper_timepart (time float8, device int, temp float);
SELECT * FROM create_hypertable('hyper_timepart', 'time', 'device', 2, time_partitioning_func => 'time_func');

-- Planner won't pick push-down aggs on table with time function
-- unless a certain amount of data
SELECT setseed(1);
INSERT INTO hyper_timepart
SELECT x, ceil(random() * 8), random() * 20
FROM generate_series(0,5000-1) AS x;

-- All partition keys covered (full partitionwise)
SET timescaledb.enable_chunkwise_aggregation = 'off';
:PREFIX
SELECT time, device, avg(temp)
FROM hyper_timepart
GROUP BY 1, 2
ORDER BY 1, 2
LIMIT 10;

:PREFIX
SELECT time_func(time), device, avg(temp)
FROM hyper_timepart
GROUP BY 1, 2
ORDER BY 1, 2
LIMIT 10;

-- Grouping on original time column should be pushed-down
SET timescaledb.enable_chunkwise_aggregation = 'on';
:PREFIX
SELECT time, device, avg(temp)
FROM hyper_timepart
GROUP BY 1, 2
ORDER BY 1, 2
LIMIT 10;

-- Applying the time partitioning function should also allow push-down
-- on open dimensions
:PREFIX
SELECT time_func(time), device, avg(temp)
FROM hyper_timepart
GROUP BY 1, 2
ORDER BY 1, 2
LIMIT 10;

-- Partial aggregation pushdown is currently not supported for this query by
-- the TSDB pushdown code since a projection is used in the path.
:PREFIX
SELECT time_func(time), _timescaledb_functions.get_partition_hash(device), avg(temp)
FROM hyper_timepart
GROUP BY 1, 2
ORDER BY 1, 2
LIMIT 10;

-- Test removal of redundant group key optimization in PG16
-- All lower versions include the redundant key on device column
:PREFIX
SELECT device, avg(temp)
FROM hyper_timepart
WHERE device = 1
GROUP BY 1
LIMIT 10;
