timescaledb/test/expected/agg_bookends-13.out
Sven Klemm ce73f25a87 Optimize first/last
This patch optimizes how first()/last() initialize the compare
function. Previously the compare function would be looked up for
every transition function call but since polymorphic types are
resolved at parse time for a specific aggregate instance the compare
function will not change during its lifetime. Additionally this
patch also fixes a memory leak when using first()/last() on
pointer types.
These changes lead to a roughly 2x speed improvement for first()/
last() and make the memory usage near-constant.

Fixes #3935
2021-12-22 08:12:11 +01:00

1186 lines
56 KiB
Plaintext

-- 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 TEST_BASE_NAME agg_bookends
SELECT format('include/%s_load.sql', :'TEST_BASE_NAME') as "TEST_LOAD_NAME",
format('include/%s_query.sql', :'TEST_BASE_NAME') as "TEST_QUERY_NAME",
format('%s/results/%s_results_optimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_OPTIMIZED",
format('%s/results/%s_results_unoptimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_UNOPTIMIZED"
\gset
SELECT format('\! diff -u --label "Unoptimized result" --label "Optimized result" %s %s', :'TEST_RESULTS_UNOPTIMIZED', :'TEST_RESULTS_OPTIMIZED') as "DIFF_CMD"
\gset
\set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)'
\ir :TEST_LOAD_NAME
-- 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.
CREATE TABLE btest(time timestamp NOT NULL, time_alt timestamp, gp INTEGER, temp float, strid TEXT DEFAULT 'testing');
SELECT schema_name, table_name, created FROM create_hypertable('btest', 'time');
schema_name | table_name | created
-------------+------------+---------
public | btest | t
(1 row)
INSERT INTO btest VALUES('2017-01-20T09:00:01', '2017-01-20T10:00:00', 1, 22.5);
INSERT INTO btest VALUES('2017-01-20T09:00:21', '2017-01-20T09:00:59', 1, 21.2);
INSERT INTO btest VALUES('2017-01-20T09:00:47', '2017-01-20T09:00:58', 1, 25.1);
INSERT INTO btest VALUES('2017-01-20T09:00:02', '2017-01-20T09:00:57', 2, 35.5);
INSERT INTO btest VALUES('2017-01-20T09:00:21', '2017-01-20T09:00:56', 2, 30.2);
--TOASTED;
INSERT INTO btest VALUES('2017-01-20T09:00:43', '2017-01-20T09:01:55', 2, 20.1, repeat('xyz', 1000000) );
CREATE TABLE btest_numeric (time timestamp NOT NULL, quantity numeric);
SELECT schema_name, table_name, created FROM create_hypertable('btest_numeric', 'time');
schema_name | table_name | created
-------------+---------------+---------
public | btest_numeric | t
(1 row)
\ir :TEST_QUERY_NAME
-- 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.
-- canary for results diff
-- this should be only output of results diff
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
setting | value
----------------------------------+-------
timescaledb.enable_optimizations | on
(1 row)
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Custom Scan (ChunkAppend) on btest (actual rows=6 loops=1)
Order: btest."time"
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=6 loops=1)
(3 rows)
:PREFIX SELECT last(temp, time) FROM btest;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
(7 rows)
:PREFIX SELECT first(temp, time) FROM btest;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time"
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
(7 rows)
:PREFIX SELECT last(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(2 rows)
:PREFIX SELECT first(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(2 rows)
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
--check whole row
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
--check toasted col
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
(7 rows)
BEGIN;
--check null value as last element
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
:PREFIX SELECT last(temp, time) FROM btest;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(9 rows)
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
:PREFIX SELECT last(temp, time) FROM btest;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(11 rows)
--check null cmp element is skipped
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=9 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
(5 rows)
-- fist returns NULL value
:PREFIX SELECT first(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=9 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
(5 rows)
-- test first return non NULL value
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
:PREFIX SELECT first(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=10 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
(6 rows)
--check non null cmp element insert after null cmp
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(7 rows)
:PREFIX SELECT first(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(7 rows)
--cmp nulls should be ignored and not present in groups
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(12 rows)
--Previously, some bugs were found with NULLS and numeric types, so test that
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1 loops=1)
Order: btest_numeric."time" DESC
-> Index Scan using _hyper_2_6_chunk_btest_numeric_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
(7 rows)
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1 loops=1)
Order: btest_numeric."time" DESC
-> Index Scan using _hyper_2_7_chunk_btest_numeric_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_2_6_chunk_btest_numeric_time_idx on _hyper_2_6_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(9 rows)
-- do index scan for last
:PREFIX SELECT last(temp, time) FROM btest;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(15 rows)
-- do index scan for first
:PREFIX SELECT first(temp, time) FROM btest;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time"
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(15 rows)
-- can't do index scan when ordering on non-index column
:PREFIX SELECT first(temp, time_alt) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(7 rows)
-- do index scan for subquery
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(15 rows)
-- can't do index scan when using group by
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
QUERY PLAN
------------------------------------------------------------------------
Sort (actual rows=2 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> HashAggregate (actual rows=2 loops=1)
Group Key: _hyper_1_1_chunk.gp
Batches: 1
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(12 rows)
-- do index scan when agg function is used in CTE subquery
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(15 rows)
-- do index scan when using both FIRST and LAST aggregate functions
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $1)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
InitPlan 2 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1 loops=1)
Order: btest_1."time"
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
(29 rows)
-- verify results when using both FIRST and LAST
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $1)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
InitPlan 2 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1 loops=1)
Order: btest_1."time"
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed)
Index Cond: ("time" IS NOT NULL)
(29 rows)
-- do index scan when using WHERE
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: (("time" IS NOT NULL) AND ("time" <= 'Fri Jan 20 09:00:02 2017'::timestamp without time zone))
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: (("time" IS NOT NULL) AND ("time" <= 'Fri Jan 20 09:00:02 2017'::timestamp without time zone))
(9 rows)
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
:PREFIX SELECT max(time), last(temp, time) FROM btest;
QUERY PLAN
------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(7 rows)
-- can't do index scan when using FIRST/LAST in ORDER BY
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
QUERY PLAN
------------------------------------------------------------------------
Sort (actual rows=1 loops=1)
Sort Key: (last(_hyper_1_1_chunk.temp, _hyper_1_1_chunk."time"))
Sort Method: quicksort
-> Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(10 rows)
-- do index scan
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=0 loops=1)
Index Cond: ("time" IS NOT NULL)
Filter: (temp < '30'::double precision)
Rows Removed by Filter: 1
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (actual rows=0 loops=1)
Index Cond: ("time" IS NOT NULL)
Filter: (temp < '30'::double precision)
Rows Removed by Filter: 1
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (actual rows=0 loops=1)
Index Cond: ("time" IS NOT NULL)
Filter: (temp < '30'::double precision)
Rows Removed by Filter: 2
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
Filter: (temp < '30'::double precision)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
Filter: (temp < '30'::double precision)
(23 rows)
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- do index scan
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time"
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed)
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
(13 rows)
-- can't do index scan when using WINDOW function
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
QUERY PLAN
------------------------------------------------------------------------
WindowAgg (actual rows=11 loops=1)
-> Sort (actual rows=11 loops=1)
Sort Key: _hyper_1_1_chunk.gp
Sort Method: quicksort
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(10 rows)
-- test constants
:PREFIX SELECT first(100, 100) FROM btest;
QUERY PLAN
--------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Result (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_2_chunk (never executed)
-> Seq Scan on _hyper_1_3_chunk (never executed)
-> Seq Scan on _hyper_1_4_chunk (never executed)
-> Seq Scan on _hyper_1_5_chunk (never executed)
(10 rows)
-- create an index so we can test optimization
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
:PREFIX SELECT last(temp, time_alt) FROM btest;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Merge Append (actual rows=1 loops=1)
Sort Key: _hyper_1_1_chunk.time_alt DESC
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_alt_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
Index Cond: (time_alt IS NOT NULL)
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_alt_idx on _hyper_1_2_chunk (actual rows=1 loops=1)
Index Cond: (time_alt IS NOT NULL)
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_alt_idx on _hyper_1_3_chunk (actual rows=1 loops=1)
Index Cond: (time_alt IS NOT NULL)
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_alt_idx on _hyper_1_4_chunk (actual rows=1 loops=1)
Index Cond: (time_alt IS NOT NULL)
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_alt_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: (time_alt IS NOT NULL)
(15 rows)
--test nested FIRST/LAST - should optimize
:PREFIX SELECT abs(last(temp, time)) FROM btest;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
Order: btest."time" DESC
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
Index Cond: ("time" IS NOT NULL)
(15 rows)
-- test nested FIRST/LAST in ORDER BY - no optimization possible
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
QUERY PLAN
-------------------------------------------------------------------------
Sort (actual rows=1 loops=1)
Sort Key: (abs(last(_hyper_1_1_chunk.temp, _hyper_1_1_chunk."time")))
Sort Method: quicksort
-> Aggregate (actual rows=1 loops=1)
-> Append (actual rows=11 loops=1)
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
(10 rows)
ROLLBACK;
-- we want test results as part of the output too to make sure we produce correct output
\set PREFIX ''
\ir :TEST_QUERY_NAME
-- 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.
-- canary for results diff
-- this should be only output of results diff
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
setting | value
----------------------------------+-------
timescaledb.enable_optimizations | on
(1 row)
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
time | gp | temp
--------------------------+----+------
Fri Jan 20 09:00:01 2017 | 1 | 22.5
Fri Jan 20 09:00:02 2017 | 2 | 35.5
Fri Jan 20 09:00:21 2017 | 1 | 21.2
Fri Jan 20 09:00:21 2017 | 2 | 30.2
Fri Jan 20 09:00:43 2017 | 2 | 20.1
Fri Jan 20 09:00:47 2017 | 1 | 25.1
(6 rows)
:PREFIX SELECT last(temp, time) FROM btest;
last
------
25.1
(1 row)
:PREFIX SELECT first(temp, time) FROM btest;
first
-------
22.5
(1 row)
:PREFIX SELECT last(temp, time_alt) FROM btest;
last
------
22.5
(1 row)
:PREFIX SELECT first(temp, time_alt) FROM btest;
first
-------
30.2
(1 row)
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
gp | last
----+------
1 | 25.1
2 | 20.1
(2 rows)
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
gp | first
----+-------
1 | 22.5
2 | 35.5
(2 rows)
--check whole row
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
gp | first
----+------------------------------------------------------------------------
1 | ("Fri Jan 20 09:00:01 2017","Fri Jan 20 10:00:00 2017",1,22.5,testing)
2 | ("Fri Jan 20 09:00:02 2017","Fri Jan 20 09:00:57 2017",2,35.5,testing)
(2 rows)
--check toasted col
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
gp | left
----+------------
1 | testing
2 | xyzxyzxyzx
(2 rows)
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
gp | last
----+------
1 | 22.5
2 | 20.1
(2 rows)
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
gp | last
----+---------
1 | testing
2 | testing
(2 rows)
BEGIN;
--check null value as last element
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
:PREFIX SELECT last(temp, time) FROM btest;
last
------
(1 row)
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
:PREFIX SELECT last(temp, time) FROM btest;
last
------
30.5
(1 row)
--check null cmp element is skipped
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
last
------
30.5
(1 row)
-- fist returns NULL value
:PREFIX SELECT first(temp, time_alt) FROM btest;
first
-------
(1 row)
-- test first return non NULL value
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
:PREFIX SELECT first(temp, time_alt) FROM btest;
first
-------
36.5
(1 row)
--check non null cmp element insert after null cmp
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
last
------
35.3
(1 row)
:PREFIX SELECT first(temp, time_alt) FROM btest;
first
-------
36.5
(1 row)
--cmp nulls should be ignored and not present in groups
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
gp | last
----+------
1 | 22.5
2 | 35.3
(2 rows)
--Previously, some bugs were found with NULLS and numeric types, so test that
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
last
------
(1 row)
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
last
------
30.5
(1 row)
-- do index scan for last
:PREFIX SELECT last(temp, time) FROM btest;
last
------
35.3
(1 row)
-- do index scan for first
:PREFIX SELECT first(temp, time) FROM btest;
first
-------
36.5
(1 row)
-- can't do index scan when ordering on non-index column
:PREFIX SELECT first(temp, time_alt) FROM btest;
first
-------
36.5
(1 row)
-- do index scan for subquery
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
last
------
35.3
(1 row)
-- can't do index scan when using group by
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
last
------
25.1
35.3
(2 rows)
-- do index scan when agg function is used in CTE subquery
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
last
------
35.3
(1 row)
-- do index scan when using both FIRST and LAST aggregate functions
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
first | last
-------+------
36.5 | 35.3
(1 row)
-- verify results when using both FIRST and LAST
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
first | last
-------+------
36.5 | 35.3
(1 row)
-- do index scan when using WHERE
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
last
------
35.5
(1 row)
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
:PREFIX SELECT max(time), last(temp, time) FROM btest;
max | last
--------------------------+------
Mon Jan 20 09:00:43 2020 | 35.3
(1 row)
-- can't do index scan when using FIRST/LAST in ORDER BY
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
last
------
35.3
(1 row)
-- do index scan
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
last
------
25.1
(1 row)
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- do index scan
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
first
-------
25.1
(1 row)
-- can't do index scan when using WINDOW function
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
gp | last
----+------
1 | 25.1
1 | 25.1
1 | 25.1
2 | 35.3
2 | 35.3
2 | 35.3
2 | 35.3
2 | 35.3
2 | 35.3
2 | 35.3
2 | 35.3
(11 rows)
-- test constants
:PREFIX SELECT first(100, 100) FROM btest;
first
-------
100
(1 row)
-- create an index so we can test optimization
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
:PREFIX SELECT last(temp, time_alt) FROM btest;
last
------
35.3
(1 row)
--test nested FIRST/LAST - should optimize
:PREFIX SELECT abs(last(temp, time)) FROM btest;
abs
------
35.3
(1 row)
-- test nested FIRST/LAST in ORDER BY - no optimization possible
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
abs
------
35.3
(1 row)
ROLLBACK;
-- diff results with optimizations disabled and enabled
\o :TEST_RESULTS_UNOPTIMIZED
SET timescaledb.enable_optimizations TO false;
\ir :TEST_QUERY_NAME
-- 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.
-- canary for results diff
-- this should be only output of results diff
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
:PREFIX SELECT last(temp, time) FROM btest;
:PREFIX SELECT first(temp, time) FROM btest;
:PREFIX SELECT last(temp, time_alt) FROM btest;
:PREFIX SELECT first(temp, time_alt) FROM btest;
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
--check whole row
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
--check toasted col
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
BEGIN;
--check null value as last element
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
:PREFIX SELECT last(temp, time) FROM btest;
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
:PREFIX SELECT last(temp, time) FROM btest;
--check null cmp element is skipped
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
-- fist returns NULL value
:PREFIX SELECT first(temp, time_alt) FROM btest;
-- test first return non NULL value
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
:PREFIX SELECT first(temp, time_alt) FROM btest;
--check non null cmp element insert after null cmp
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
:PREFIX SELECT first(temp, time_alt) FROM btest;
--cmp nulls should be ignored and not present in groups
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
--Previously, some bugs were found with NULLS and numeric types, so test that
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
-- do index scan for last
:PREFIX SELECT last(temp, time) FROM btest;
-- do index scan for first
:PREFIX SELECT first(temp, time) FROM btest;
-- can't do index scan when ordering on non-index column
:PREFIX SELECT first(temp, time_alt) FROM btest;
-- do index scan for subquery
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
-- can't do index scan when using group by
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
-- do index scan when agg function is used in CTE subquery
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
-- do index scan when using both FIRST and LAST aggregate functions
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
-- verify results when using both FIRST and LAST
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
-- do index scan when using WHERE
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
:PREFIX SELECT max(time), last(temp, time) FROM btest;
-- can't do index scan when using FIRST/LAST in ORDER BY
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
-- do index scan
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- do index scan
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- can't do index scan when using WINDOW function
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
-- test constants
:PREFIX SELECT first(100, 100) FROM btest;
-- create an index so we can test optimization
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
:PREFIX SELECT last(temp, time_alt) FROM btest;
--test nested FIRST/LAST - should optimize
:PREFIX SELECT abs(last(temp, time)) FROM btest;
-- test nested FIRST/LAST in ORDER BY - no optimization possible
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
ROLLBACK;
\o
\o :TEST_RESULTS_OPTIMIZED
SET timescaledb.enable_optimizations TO true;
\ir :TEST_QUERY_NAME
-- 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.
-- canary for results diff
-- this should be only output of results diff
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
:PREFIX SELECT last(temp, time) FROM btest;
:PREFIX SELECT first(temp, time) FROM btest;
:PREFIX SELECT last(temp, time_alt) FROM btest;
:PREFIX SELECT first(temp, time_alt) FROM btest;
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
--check whole row
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
--check toasted col
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
BEGIN;
--check null value as last element
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
:PREFIX SELECT last(temp, time) FROM btest;
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
:PREFIX SELECT last(temp, time) FROM btest;
--check null cmp element is skipped
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
-- fist returns NULL value
:PREFIX SELECT first(temp, time_alt) FROM btest;
-- test first return non NULL value
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
:PREFIX SELECT first(temp, time_alt) FROM btest;
--check non null cmp element insert after null cmp
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
:PREFIX SELECT last(temp, time_alt) FROM btest;
:PREFIX SELECT first(temp, time_alt) FROM btest;
--cmp nulls should be ignored and not present in groups
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
--Previously, some bugs were found with NULLS and numeric types, so test that
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
--check non-null element "overrides" NULL because it comes after.
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
-- do index scan for last
:PREFIX SELECT last(temp, time) FROM btest;
-- do index scan for first
:PREFIX SELECT first(temp, time) FROM btest;
-- can't do index scan when ordering on non-index column
:PREFIX SELECT first(temp, time_alt) FROM btest;
-- do index scan for subquery
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
-- can't do index scan when using group by
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
-- do index scan when agg function is used in CTE subquery
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
-- do index scan when using both FIRST and LAST aggregate functions
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
-- verify results when using both FIRST and LAST
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
-- do index scan when using WHERE
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
:PREFIX SELECT max(time), last(temp, time) FROM btest;
-- can't do index scan when using FIRST/LAST in ORDER BY
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
-- do index scan
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- do index scan
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
-- can't do index scan when using WINDOW function
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
-- test constants
:PREFIX SELECT first(100, 100) FROM btest;
-- create an index so we can test optimization
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
:PREFIX SELECT last(temp, time_alt) FROM btest;
--test nested FIRST/LAST - should optimize
:PREFIX SELECT abs(last(temp, time)) FROM btest;
-- test nested FIRST/LAST in ORDER BY - no optimization possible
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
ROLLBACK;
\o
:DIFF_CMD
--- Unoptimized result
+++ Optimized result
@@ -1,6 +1,6 @@
setting | value
----------------------------------+-------
- timescaledb.enable_optimizations | off
+ timescaledb.enable_optimizations | on
(1 row)
time | gp | temp